シナリオは次のとおりです。Addresses テーブルとの 1 対多の関係を持つ Persons テーブルがあり、Address 行の 1 つが「プライマリ」Address です。
正規化されたスキーマでは、
- Persons.PrimaryAddressID を使用して、個人の「プライマリ」アドレスにアクセスします
また
- Addresses.IsPrimary ビット列を使用して、Addresses.PersonID を介して個人の「プライマリ」アドレスを参照します。
また
- 他の
なぜ?
シナリオは次のとおりです。Addresses テーブルとの 1 対多の関係を持つ Persons テーブルがあり、Address 行の 1 つが「プライマリ」Address です。
正規化されたスキーマでは、
また
また
なぜ?
それは、個人とアドレスの関係が 1 対 0 プラスか、1 対 1 プラスかによって異なります。
プライマリ アドレスが必要な場合は、Persons テーブル自体に入力します (必須属性であるため)。
一方、アドレスなしで人がスキーマに存在できる場合は、Addressesテーブル内のすべてのアドレスを同じままにして、テーブルの属性を使用しPersonsてプライマリ (NULL または関連するAddresses行へのポインター) を選択します。
アドレスのプライマリティをテーブルに保存する場合Addresses、Bob Smith の 2 つのアドレスが両方ともプライマリであると主張している場合はどうしますか? トリガーでそれを止めることもできますが、スキーマを適切に設計する方がはるかに効率的です。
そして、2 人のルームメイトが同じ住所を共有しているが、1 人はずっとそこに住んでいて、もう 1 人はほとんどの時間をガールフレンドと一緒に過ごしているとしたら、どうなるでしょうか? プライマリティが Addresses テーブルにある場合、個人間で住所行を共有することはできません。
私が理解しようとしているのは、属性を適切なオブジェクトに割り当てる必要があるということです。個人のプライマリ アドレスは、アドレスではなく個人に属します。
最大限の効率と柔軟性を得るには、次のスキーマを使用します。
Persons:
Id primary key
PrimaryAddressId
OtherStuff
Addresses:
Id primary key
OtherStuff
PersonAddresses:
Id primary key
PersonId foreign key on Persons(Id)
AddressId foreign key on Addresses(Id)
Persons.PrimaryAddressIdハング ポインターである可能性がある小さなデータ整合性の問題があります。許可したいので、主キーの1つへの外部キーにすることはできませんNULL。つまり、存在しない を指している可能性に対応する必要がありますAddresses.Id。
Addresses関連するPersons行が更新されるように、削除前のトリガーとしてそれを修正するだけです( PrimaryAddressidNULLに設定)。
または、Addressesテーブルに "不明" のアドレスを 1 つ持つようにして、すべての行にPersons少なくとも 1 つのアドレスが含まれるようにすることもできますPrimaryAddressid。
次に、それを適切な制約付きの関係にして、SQL をいくらか単純化できます。プラグマティズムは、現実の世界ではドグマティズムを打ち負かすことがよくあります:-)
「Persons.PrimaryAddressID を使用して、個人の「プライマリ」アドレスにアクセスする」に行きます。Primary Address は、Person が Addresses にリンクされている場合にのみ意味を持ちます。したがって、それは Person に属している必要があります。2 番目のアプローチが失敗する次のシナリオについて考えてみてください。
a) Addresses.IsPrimary が意味を持たない人物を参照せずに、住所が別のエンティティで使用されている。
b) 同じアドレスが 2 人によって使用され、1 人目はプライマリとして使用され、2 人目はそうではありません。
1 人が最大で 1 つのプライマリ アドレスを持っているという制約が必要な場合は、a のPersons.PrimaryAddressID方が明らかに単純です。実際、スキーマ自体によって強制されます。また、1 人あたり1 つのプライマリ アドレスを正確に確保することも簡単です (その列を null にしないようにするだけです)。必要に応じて、2 人がプライマリ アドレスを共有できないようにすることもできます (その列を一意にするだけです)。
もちろん、このアプローチはそのような単純な制約を強制するのに優れているため、これらの制約が不要な場合は良くありません。たとえば、ある人が複数の「プライマリ」アドレスを持つことを可能にしたい場合、問題のアプローチは機能しません。
ちなみに、 2 人の人が同じアドレスを共有できないという事実を強制したい場合を除き、1 人/複数アドレスの関係が特に良いとは考えていません。人のテーブル、住所の 1 つ、および関係の 1 つ (多くの人が同じ住所を共有することができ、実際には実際に共有するため、ほとんどのコンテキストでは自然に多対多になります)。
このルートを選択した場合、特に高い柔軟性 (複数のプライマリ アドレス &c) が必要な場合は、関係テーブルに「primality」ブール値を保持させることは魅力的な選択です (それでも、いくつかを強制することはそれほど難しくありません)ただし、「アドレスは少なくとも 1 人の人物に属している」またはその逆などの他の制約は、単純に表現するのが難しい場合があります)。
覚えておくべき教訓: スキーマが表現する必要がある制約を正確に表現すると、その目的に適したスキーマが明確に現れることがよくあります。関心のある制約が謎である場合、「正しいスキーマは何か」という質問への答えも謎です;-)。