私は、1対多の関係を持ちたいと考えています。各親について、1人または0人の子供が「お気に入り」としてマークされています。ただし、すべての親に子供がいるわけではありません。 (このサイトでは、両親は質問、子供は回答、お気に入りは承認された回答と考えてください。)たとえば、
TableA
Id INT PRIMARY KEY
TableB
Id INT PRIMARY KEY
Parent INT NOT NULL FOREIGN KEY REFERENCES TableA.Id
それを見ると、次の列をTableAに追加できます。
FavoriteChild INT NULL FOREIGN KEY REFERENCES TableB.Id
または次の列をTableBに追加します。
IsFavorite BIT NOT NULL
最初のアプローチの問題は、私が理解しているように、正規化された形式ではないnull可能な外部キーを導入することです。 2番目のアプローチの問題は、多くても1人の子供がお気に入りになるように、さらに多くの作業を行う必要があることです。
使用するアプローチを決定するには、どのような基準を使用する必要がありますか?または、私が考慮していない他のアプローチはありますか?
SQL Server 2012を使用しています。
別の方法(ヌルなしで、FOREIGN KEY
関係に循環なし)は、「お気に入りの子」を格納するための3番目のテーブルを持つことです。ほとんどのDBMSでは、UNIQUE
に追加のTableB
制約が必要です。
@Aaronは、上記の命名規則がやや面倒であり、エラーにつながる可能性があることを特定するのが速かった。テーブル全体にId
列がなく、表示される多くのテーブルで(結合されている)列の名前が同じである場合は、通常はより適切です(そして正気を保ちます)。だから、これは名前の変更です:
Parent
ParentID INT NOT NULL PRIMARY KEY
Child
ChildID INT NOT NULL PRIMARY KEY
ParentID INT NOT NULL FOREIGN KEY REFERENCES Parent (ParentID)
UNIQUE (ParentID, ChildID)
FavoriteChild
ParentID INT NOT NULL PRIMARY KEY
ChildID INT NOT NULL
FOREIGN KEY (ParentID, ChildID)
REFERENCES Child (ParentID, ChildID)
(使用している)SQL-Serverでは、言及したIsFavorite
ビット列のオプションもあります。親ごとの一意のお気に入りの子は、フィルターされた一意のインデックスを介して達成できます。
Parent
ParentID INT NOT NULL PRIMARY KEY
Child
ChildID INT NOT NULL PRIMARY KEY
ParentID INT NOT NULL FOREIGN KEY REFERENCES Parent (ParentID)
IsFavorite BIT NOT NULL
CREATE UNIQUE INDEX is_FavoriteChild
ON Child (ParentID)
WHERE IsFavorite = 1 ;
また、少なくともSQL-Serverではオプション1が推奨されない主な理由は、外部キー参照の循環パスのパターンに問題があるためです。
かなり古い記事を読む:SQL By Design:循環参照
2つのテーブルから行を挿入または削除すると、「鶏と卵」の問題が発生します。制約に違反することなく、どのテーブルを最初に挿入する必要がありますか?
これを解決するには、少なくとも1つの列をnull許容として定義する必要があります。 (つまり、厳密に言うと、すべての列をNOT NULL
として使用できますが、遅延可能な制約を実装しているPostgresやOracleなどのDBMSでのみ可能です。同様の質問で@Erwinの回答を参照してください:SQLAlchemyの複雑な外部キー制約これをPostgresで実行する方法について)。それでも、このセットアップは薄い氷の上でスケートをしているように感じます。
SO(MySQLの場合)SQLでは、2つのテーブルが相互に参照しても問題ないか)でほぼ同じ質問も確認してください。ここで私の答えはほとんど同じですが、MySQLには部分的なインデックスがありません。そのため、実行可能なオプションはnull可能なFKと追加のテーブルソリューションのみです。
それはあなたの優先順位が何であるかに依存します。仕事を避けたいですか、それとも正規化の最も厳しいルールを守りたいですか?
個人的には、子テーブルにIsFavorite
を含めることをお勧めします。また、親ごとに多くても1つの子がその親のお気に入りになるように、喜んで仕事に取り入れるでしょう。主な理由は、null許容の外部キーの事柄とは何の関係もありません。私は、両方向を指している外部キーのすべてについての考えが好きではありません。
@ypercubeの提案も、適切な妥協案です。
余談ですが、Id
のような無意味な列名でスキーマを散らかさないでください。むしろ、スキーマ全体でId
に意味のある名前を付けたいと思います。著者を特定していますか? OK、それをAuthorID
と呼びます。製品を表すものですか? OK、ProductID
。それは従業員ですか、場合によってはマネージャーを参照しますか?わかりました、EmployeeID
とManagerID
は、ID
とParent
よりも意味があります。それを省略することは論理的に思えるかもしれませんが(それを入れるのは冗長です)、複雑な結合(またはここでクエリを投稿)を書き始めると、そのポイントの一連の結合をリバースエンジニアリングしようとするときに、いくつかの呪いを確実に感じるでしょう。 a.Parent = b.ID
... blecchのような列に。
データは子テーブルに属します。テーブルのトリガーを使用してそれを正しい状態に保ち、1つのレコードだけがお気に入り(またはこの場合は優先アドレス)としてマークされるようにします。
ただし、@ ypercubeの別のテーブルの考え方も良いものです。