特権のある子供と1対多の関係を築く方法は?


22

各親について、1人または0人の子供が「お気に入り」としてマークされる1対多の関係が必要です。ただし、すべての親が子供を持つわけではありません。(このサイトでは、両親を質問、子供を回答、お気に入りを受け入れられた回答と考えてください。)たとえば、

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を使用しています。

回答:


19

別の方法(Nullなし、および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:The Circular Reference」を読んでください。

2つのテーブルの行を挿入または削除すると、「鶏と卵」の問題が発生します。制約に違反せずに、最初にどのテーブルを挿入する必要がありますか?

これを解決するには、少なくとも1つの列をNULL可能に定義する必要があります。(OK、技術的にあなたは、あなたがすべての列を持つことができますする必要はありませんとしてNOT NULLだけ遅延可能制約を実装している同様の問題のアーウィンの答え@参照のPostgresやOracleなどのDBMS、中:。SQLAlchemyの中に複雑な外部キー制約方法についてこれはPostgresで実行できます)。それでも、このセットアップは薄い氷の上でスケートをするように感じます。

また、SOでほぼ同じ質問を確認してください(ただし、MySQLの場合)SQLでは、2つのテーブルが相互に参照しても問題ありませんか?私の答えはほぼ同じです。ただし、MySQLには部分インデックスがないため、実行可能なオプションはNULL可能FKと追加のテーブルソリューションのみです。


9

それはあなたの優先順位によって異なります。仕事を避けたいですか、または正規化の最も厳しい規則を守りたいですか?

個人的にIsFavoriteは、子テーブルに入れる方が良いと思います。そして、親ごとにせいぜい1人の子がその親のお気に入りであることを確認するために喜んで仕事をするでしょう。主な理由は、null許容の外部キーとは関係ありません。両方向を指す外部キーのすべてのアイデアが好きではありません。

@ypercubeの提案も同様に良い妥協案です。

余談ですが、スキーマのような意味のない列名でスキーマを散らかさないでくださいIdIdスキーマ全体で意味のある方法で名前が付けられることを望んでいます。著者を特定していますか?OK、それを呼び出しますAuthorID。製品を表していますか?わかりましたProductID。それは従業員ですか、場合によってはマネージャーを参照していますか?[OK]を、EmployeeIDそしてManagerIDより私に多くの意味をなすIDParent。それを省く(そしてそれを入れるのは冗長)のは論理的に思えるかもしれませんが、複雑な結合を書き始める(またはここにクエリを投稿する)とき、そのポイントの結合をリバースエンジニアリングしようとすると、間違いなくいくらかの呪いを感じるでしょうa.Parent = b.ID... blecchのような列に。


1

データは子テーブルに属します。テーブル上でトリガーを使用して正しい状態を保ち、唯一のレコードがお気に入りとしてマークされるようにします(この例では優先アドレスとしてマークされます)。

ただし、@ ypercubeの独立したテーブルという考え方も同様に良いものです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.