2

想象一下,我们有一张国家表和一张城市表。一个国家当然可以有很多城市,但一个城市只能在一个国家,因此一对多的关系很直观:

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. 添加一个不能为空capital的int 列。countries优点:总是只有一个城市;缺点:与城市无关,没有强制城市在乡村,或者它甚至存在。

  2. capital向中添加一个布尔列cities,如果为 true,则表明该城市是相关国家的首都。Pro:直接与相关城市相关联,没有重复的列表示层次结构;缺点:很确定这是一个糟糕的正常化,因为没有什么可以阻止给定国家的“首都”为零或不止一个。

  3. 创建一个附加表capitals,其中包含列countrycity两个列(或至少在 上city)的唯一约束。优点:感觉更干净,很容易加入countriescities;缺点:仍然不能确保城市在国内,或者两者都存在。

表示这种关系的最规范和/或最佳方式是什么?有什么办法可以确保每个国家都有一个确实存在并位于该国境内的资本?我想这是不可能的,在这种情况下,我怎样才能最好地减少我的客户端代码的问题?

我目前正在使用 SQLite,但无论底层数据库如何,我都对通用答案感兴趣。

我做了一些挖掘,发现在数据库中指示主要/默认记录,但我认为这并不能真正回答我的问题。


PS:没有首都也没那么糟糕(可能没有城市!),但如果有多个就糟糕了。

4

3 回答 3

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)
);

在该表中,主键约束保证每个国家不能有超过一个资本。外键约束保证您选择的资本存在于您选择的国家。在引用的表(“城市”表)中,您还需要对 {city_id, country_id} 的唯一约束;由于 {city_id} 在“cities”表中是唯一的,{city_id, country_id} 在该表中也必然是唯一的,所以这不是问题。

保证国家和首都之间的一对一关系(不是一对零或一的关系)的声明性“方式”是使用断言。但我不知道当前有任何支持 CREATE ASSERTION 的 SQL dbms。这迫使我们依赖其中的一项或多项:

  • 触发器和可能的延迟约束,
  • 应用程序代码,或
  • 行政程序。

(最初,您必须在单个事务中在三个表“国家”、“城市”和“首都”中输入一行以满足所有约束。我认为您需要延迟约束,但我今天还没喝咖啡。)

于 2012-08-16T12:27:28.603 回答
0

为了清楚和简单起见,我将布尔 IsCapital 列添加到城市表中。然后添加一个触发器,当 IsCapital 在记录上设置为 true 时,设置所有其他城市(共享更新记录的国家/地区)IsCapital = false。这将解决您的大部分问题。确保每个国家只有一个首都的一种情况实际上是不可能的,你可以确保有 0 或 1,但由于城市表对国家有 FK 约束,总会有一个时间点插入的国家将没有可以设置为首都的城市。

FWIW,我认为逻辑应该留给应用程序,数据库的引用完整性。

于 2012-08-16T04:36:12.383 回答
0

要确保每个国家/地区只有一个首都且首都不是来自不同国家/地区的城市,请执行以下操作:

在此处输入图像描述

请注意我们如何使用标识关系将 COUNTRY_ID 迁移到 CITY 的 PK,因此可以将其迁移回 CONTRY - 这是保证首都必须实际属于其首都的国家的原因。

此处的循环引用可防止插入新数据,如果 DBMS 支持,则使用延迟外键解决。否则,您可以只保留 COUNTRY.CAPITAL_NO NULL-able(并在应用程序级别强制其最终非 NULL-ness)。1


1这假设 DBMS 具有 MATCH SIMPLE 外键(即,如果 FK 的任何组件为 NULL,则忽略 FK)。如果 DBMS 仅支持 MATCH PARTIAL 或 FULL(例如 MS Access),那么您就不走运了,并且必须通过非声明性方式(触发器或应用程序代码)来模拟 FK。

于 2012-08-21T02:32:08.613 回答