web-dev-qa-db-ja.com

恵まれた子供と1対多の関係を持つ方法は?

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

22
cubetwo1729

別の方法(ヌルなしで、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と追加のテーブルソリューションのみです。

19
ypercubeᵀᴹ

それはあなたの優先順位が何であるかに依存します。仕事を避けたいですか、それとも正規化の最も厳しいルールを守りたいですか?

個人的には、子テーブルにIsFavoriteを含めることをお勧めします。また、親ごとに多くても1つの子がその親のお気に入りになるように、喜んで仕事に取り入れるでしょう。主な理由は、null許容の外部キーの事柄とは何の関係もありません。私は、両方向を指している外部キーのすべてについての考えが好きではありません。

@ypercubeの提案も、適切な妥協案です。

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

9
Aaron Bertrand

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

ただし、@ ypercubeの別のテーブルの考え方も良いものです。

1
HLGEM