2

国のテーブルと都市のテーブルがあるとします。もちろん、国は多くの都市を持つことができますが、都市は 1 つの国にしか存在できないため、1 対多の関係は直感的に理解できます。

countries
| id | name    |
|  1 | Lorwick |
|  2 | Belmead |

cities
| id | country | name        |
|  1 |       1 | Marblecrest |
|  2 |       1 | Westacre    |
|  3 |       2 | Belcoast    |
|  4 |       1 | Rosemarsh   |
|  5 |       2 | Vertston    |

しかし、1 対多の関係に加えて、首都の 1 対 1 の関係についても説明したいと思います。それが重要な場合、首都はかなり定期的に変更される可能性があり、さらに言えば、都市は意のままに現れたり消えたりし、その都市は国を切り替える可能性があると想定してください。ポイントは、このデータは不安定です。

いくつかのオプションが表示されます。

  1. null にできないint 列capitalを追加します。countries長所: 常に正確に 1 つの都市。短所: 都市に関連付けられていない、都市を強制するものは何も国にない、または都市が存在することさえありません。

  2. ブール列capitalを に追加します。truecitiesの場合、その都市が関連する国の首都であることを示します。長所: 問題の都市に直接関連付けられており、階層を示す列が重複していません。短所:特定の国の「首都」がゼロまたは複数であることを止めるものは何もないため、これは正規化が不十分であることは間違いありません。

  3. capitalscountryとを含む追加のテーブルを作成し、city両方の列 (または少なくともcity) に一意の制約を設定します。countries良い点: どちらでもすっきりと簡単に参加できcitiesます。短所:都市が国にあること、またはそのいずれかが存在することはまだ保証されていません。

この関係を表す最も正規化された、または最良の方法は何ですか? 各国に、実際に存在し、その国の中に存在する首都が 1 つだけあることを確認する方法はありますか? それは不可能だと思いますが、その場合、クライアント コードの問題を最小限に抑えるにはどうすればよいでしょうか?

私は現在 SQLite を使用していますが、基礎となるデータベースに関係なく、一般化された回答に興味があります。

少し掘り下げて、Indicating primary/default record in databaseを見つけましたが、これが本当に私の質問に答えているとは思いません。


PS: 首都がなくても問題ありませんが (都市がなくてもかまいません!)、複数あると大変です。

4

3 に答える 3

1

「それぞれの国に首都が1つだけある」という要件は、「都市は自由に現れては消える」という要件と矛盾すると思います。都市が消滅する可能性がある場合、首都も消滅する可能性があります。

首都のテーブルに外部キー制約を使用して、「各国には、実際に存在し、その国の中に存在する資本が [ゼロまたは] 1 つある」という制約を適用できます。

create table capitals (
  country_id integer primary key,
  city_id integer not null,
  foreign key (country_id, city_id) references cities (country_id, city_id)
);

そのテーブルでは、主キー制約により、国ごとに複数の首都が存在しないことが保証されています。外部キー制約により、選択した資本が選択した国に存在することが保証されます。参照されるテーブル ("cities" テーブル) では、{city_id, country_id} に対する一意の制約も必要です。{city_id} は "cities" テーブルで一意であるため、{city_id, country_id} もそのテーブルで必ず一意になるため、問題はありません。

国と首都の間の 1 対 1 の関係 (1 対 0 または 1 の関係ではない) を保証する宣言的な「方法」は、アサーションを使用することです。しかし、CREATE ASSERTION をサポートする現在の SQL dbms は知りません。そのため、次の 1 つ以上に依存する必要があります。

  • トリガーおよび場合によっては延期された制約、
  • アプリケーションコード、または
  • 行政手続き。

(最初は、すべての制約を満たすために、単一のトランザクションで「countries」、「cities」、および「captials」の 3 つのテーブルに行を入力する必要があります。そのためには、遅延制約が必要になると思います。でも今日はまだコーヒーを飲んでいません。)

于 2012-08-16T12:27:28.603 に答える
0

国ごとに 1 つの首都があり、その首都が別の国の都市でないことを確認するには、次のようにします。

ここに画像の説明を入力

識別関係を使用して COUNTRY_ID を CITY の PK に移行する方法に注意してください。これにより、それを CONTRY に戻すことができます。これにより、首都が実際に首都である国に属していなければならないことが保証されます。

ここでの循環参照は、DBMS がサポートしている場合、遅延外部キーを使用して解決される新しいデータの挿入を防ぎます。それ以外の場合は、COUNTRY.CAPITAL_NO を NULL 可能のままにしておくことができます (そして、アプリケーション レベルでその最終的な非 NULL 性を強制します)。1


1これは、DBMS に MATCH SIMPLE 外部キーがあることを前提としています (つまり、そのコンポーネントのいずれかが NULL の場合、FK は無視されます)。DBMS が MATCH PARTIAL または FULL (MS Access など) のみをサポートしている場合、運が悪く、非宣言的な手段 (トリガーまたはアプリケーション コード) を介して FK をエミュレートする必要があります。

于 2012-08-21T02:32:08.613 に答える
0

わかりやすくシンプルにするために、boolean の IsCapital 列を都市テーブルに追加します。次に、レコードで IsCapital が true に設定されている場合に、他のすべての都市 (更新されたレコードの国を共有する) IsCapital = false を設定するトリガーを追加します。これにより、ほとんどの懸念が解決されます。国ごとに首都が 1 つだけであることを保証する 1 つのケースは実際には不可能です。0 または 1 であることを保証することはできますが、citys テーブルには国への FK 制約があるため、常に特定の時点が存在します。挿入された国には、首都として設定できる都市がありません。

FWIW、ロジックはアプリに、参照整合性はデータベースに任せるべきだと思います。

于 2012-08-16T04:36:12.383 に答える