0または1から0または1


9

Sql Serverでゼロまたは1対ゼロまたは1の関係を最も自然な方法でモデル化するにはどうすればよいですか?

サイトの危険をリストした「ハザード」テーブルがあります。サイトで実行する必要がある作業用の「タスク」テーブルがあります。一部のタスクはハザードを修正することであり、タスクは複数のハザードを処理することはできません。一部のハザードにはそれらを修正するタスクがあります。ハザードには2つのタスクを関連付けることができません。

以下は私が考えることができる最高のものです:

CREATE TABLE [dbo].[Hazard](
  [HazardId] [int] IDENTITY(1,1) NOT NULL,
  [TaskId] [int] NULL,
  [Details] [varchar](max) NULL,
 CONSTRAINT [PK_Hazard] PRIMARY KEY CLUSTERED 
(
  [HazardId] ASC
))

GO

ALTER TABLE [dbo].[Hazard]  WITH CHECK ADD  CONSTRAINT [FK_Hazard_Task] FOREIGN KEY([TaskId])
REFERENCES [dbo].[Task] ([TaskId])
GO


CREATE TABLE [dbo].[Task](
  [TaskId] [int] IDENTITY(1,1) NOT NULL,
  [HazardId] [int] NULL,
  [Details] [varchar](max) NULL,
 CONSTRAINT [PK_Task] PRIMARY KEY CLUSTERED 
(
  [TaskId] ASC
))

GO

ALTER TABLE [dbo].[Task]  WITH CHECK ADD  CONSTRAINT [FK_Task_Hazard] FOREIGN KEY([HazardId])
REFERENCES [dbo].[Hazard] ([HazardId])
GO

それを別の方法で行いますか?この設定に不満なのは、タスクとハザードが他のタスクとハザードではなく、お互いを指すようにし、同じハザード/タスクを指すタスク/ハザードがないことを確認するためのアプリケーションロジックが必要なためです。別のタスク/ハザードが指します。

もっと良い方法はありますか?

ここに画像の説明を入力してください


HazardテーブルのTaskIDに一意のインデックスとTaskテーブルのHazardIDの一意のインデックスを作成できなかった理由はありますか?それはあなたがテーブルにそれらのうちの1つだけを持つことができるようにそれを作るでしょう、それはあなたが達成しようとしていることだと私は思います。
mskinner 2015

@mskinnerは一意ではありませんが、多くの場合は一意nullです。
Andrew Savinykh

ああ、落とし穴。その場合、Ben-Gan氏は、複数のnullを許可するための制約を作成する方法について、ここ sqlmag.com/sql-server-2008/unique-constraint-multiple-nullsですばらしい記事を書いています。それでうまくいくと思います。そうでない場合はお知らせください。
mskinner 2015

補足として、フィルター選択されたインデックスの使用には多くの問題があるため、慣れていない場合は、それらを読んでおくとよいでしょう。これに関する良いブログがあります。 blogs.msdn.com/b/sqlprogrammability/archive/2009/06/29/… 、しかし、この特定のシナリオでは、他の問題があまり悲しみを引き起こさないと想定すれば、それはうまくいくかもしれません。
mskinner 2015

FWIW一意のフィルター処理されたインデックスは、NULL以外の行にのみ一意性を適用できます。例:CREATE UNIQUE INDEX x ON dbo.Hazards(TaskID) WHERE TaskID IS NOT NULL;
Aaron Bertrand

回答:


9

あなたが行くことができる、独自のアイデア現在のセット・アップから、外部キーのいずれかを除去することにより、非対称スキーマの、または、対称物事を保つために、あなたは両方の外部キーを削除し、導入する可能性接合テーブルをそれぞれ参照の一意性制約で。

したがって、次のようになります。

CREATE TABLE dbo.Hazard
(
  HazardId int IDENTITY(1,1) NOT NULL CONSTRAINT PK_Hazard PRIMARY KEY CLUSTERED,
  Details varchar(max) NULL
);

CREATE TABLE dbo.Task
(
  TaskId int IDENTITY(1,1) NOT NULL CONSTRAINT PK_Task PRIMARY KEY CLUSTERED,
  Details varchar(max) NULL,
);

CREATE TABLE dbo.HazardTask
(
  HazardId int NOT NULL
    CONSTRAINT FK_HazardTask_Hazard FOREIGN KEY REFERENCES dbo.Hazard (HazardId)
    CONSTRAINT UQ_HazardTask_Hazard UNIQUE,
  TaskId int NOT NULL
    CONSTRAINT FK_HazardTask_Task FOREIGN KEY REFERENCES dbo.Task (TaskId)
    CONSTRAINT UQ_HazardTask_Task UNIQUE
);

(HazardId, TaskId)これらの組み合わせを別のテーブルから参照する必要がある場合は、さらに主キーであることを宣言できます。ただし、ペアを一意に保つために、主キーは不要です。各IDが一意であれば十分です。


1
+1これがそのような関係を実装する最も自然な方法だと思います。しかし、通常は、最小の場合にのみ、タプルをプライマリとして選択します。タプル(HazardId, TaskId)にはタプルが(HazardId)あり(TaskId)、両方ともこのテーブルの行を一意に識別します。それらの1つを主キーとして選択する必要があります。
miracle173 2015

2

総括する:

  • ハザードのタスクは1つまたはゼロ
  • タスクには1つまたはゼロのハザードがあります

タスクテーブルとハザードテーブルが別の目的で使用されている場合(つまり、タスクやハザードに他のデータが関連付けられており、表示されたモデルが簡略化されて関連フィールドのみが表示されている場合)、あなたのソリューションは正しいと思います。

それ以外の場合、タスクとハザードが互いに結合するためだけに存在する場合、2つのテーブルは必要ありません。次のフィールドを使用して、リレーションシップの単一のテーブルを作成できます。

ID            int, PK
TaskID        int, (filtered) unique index  
TaskDetails   varchar
HazardID      int, (filtered) unique index
HazardDetails varchar

1
いずれの場合も、フィルター処理されたインデックスであることを明確にするために、自由に選択しました(問題の説明から推測できるかもしれませんが、完全に明らかではない場合があります)。不要だと感じた場合、または別の方法でアイデアを伝えたい場合は、自由に編集してください。
Andriy M

私はまだこの考えに本当に満足していないと言わざるを得ません。問題は、TaskIDとHazardIDを手動で生成する必要があることです。これが2012年だった場合、シーケンスを使用することは可能ですが、それでも私には正しくないようです。タスクとハザードの2つは完全に異なるエンティティであるため、それらを1つのテーブルに格納するという考えと調和させるのは難しいと思います。
Andriy M

このデザインを選択した場合、なぜTaskIDand が必要なのHazardIDですか?あなたは、2つのBIT列、持っている可能性がありIsTaskIsHazardそしてないそれらの両方が偽である制約を。次に、Hazardテーブルは単にビューCRAETE VIEW Hazard SELECT HazardID = ID, HazardDetails, ... FROM ThisTable WHERE IsHazard = 1;とタスクです。
ypercubeᵀᴹ

@ypercubeテーブルに形のないものを格納し、バイナリフィールドを使用して、タスクかハザードかを判断します。それは醜いデータベースのモデリングです。
dr_

@AndriyM私は理解し、ほとんどの場合、同じテーブルに2種類の異なるエンティティを格納するべきではないことに同意します。ただし、私の注意事項をお読みください。タスクとハザードが1対1の関係にあり、このためにのみ存在する場合、単一のテーブルを使用することは許容されます。
dr_

1

まだ言及されていないように思われる別のアプローチは、ハザードとタスクに同じIDスペースを使用させることです。ハザードにタスクがある場合、そのIDは同じになります。タスクがハザード用である場合、同じIDになります。

これらのIDを設定するには、ID列ではなくシーケンスを使用します。

このタイプのデータモデルのクエリでは、(完全)外部結合を使用して結果を取得します。

このアプローチは、@ AndriyMの回答と非常に似ていますが、彼の回答ではIDが異なることと、この関係を格納するテーブルが許可されています。

このアプローチを2つのテーブルのシナリオで使用する必要があるかどうかはわかりませんが、関係するテーブルの数が増えるとうまくいきます。


貢献してくれてありがとう、私もそのアプローチを検討しました。シーケンスはsql2008に実装するのが面倒であり、我慢したいと思っているよりも多くの問題を抱えているため、最終的には反対しました。
アンドリューSavinykh
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.