在公司表中添加primaryLocationID吗?
是的,但这会创建一个循环引用,可能会阻止您插入新数据。
![enter image description here](https://istack.dev59.com/FlnQn.webp)
解决这个“鸡生蛋”的问题的一种方法是将
Company.PrimaryLocationID
设为可空,这样就可以暂时禁用其中一个循环外键。不幸的是,这意味着数据库只能强制实施“1:0..1”,而不能严格实施“1:1”关系(所以你必须在应用程序代码中强制执行它)。
然而,如果你的DBMS支持延迟约束(如Oracle或PostgreSQL),你可以简单地推迟其中一个FK来打破循环,同时事务仍在进行中。到事务结束时,两个FK都必须存在,从而产生真正的“1:1”关系。
另一种解决方案是在
Locations
表中设置一个标志来表示主要位置,并将非主要位置设置为NULL(请注意
U1
,表示唯一约束条件,确保公司不能有多个主要位置):
![enter image description here](https://istack.dev59.com/ZsZEX.webp)
CREATE TABLE Location (
LocationID INT PRIMARY KEY,
CompanyID INT NOT NULL,
LocationName VARCHAR(50) NOT NULL,
IsPrimary INT CHECK (IsPrimary IS NULL OR IsPrimary = 1),
CONSTRAINT Locations_U1 UNIQUE (CompanyID, IsPrimary)
);
不幸的是,这存在一些问题:
- 即使在支持延迟约束的数据库管理系统上,它也只能保证最多"1:0..1"(但不是真正的"1:1")。
- 它需要一个额外的索引(以支持唯一约束)。每个索引都会带来一定的开销,主要是对插入/更新/删除性能的影响。此外,在聚簇表中的二级索引包含PK的副本,这可能会使它们比预期更"胖"。
- 它依赖于符合ANSI标准的复合唯一约束,如果任何(但不一定是所有)字段为空,则允许重复行。不幸的是,并非所有的DBMS都遵循该标准,因此上述方法在Oracle或MS SQL Server下不能直接使用(但在PostgreSQL和MySQL下可以使用)。您可以使用过滤唯一索引代替唯一约束来解决这个问题,但并非所有DBMS都支持。
该段文字的大意是:
BaBL86's solution模型是M:N,而你的要求似乎是1:N。尽管如此,该模型可以通过在
{LocationID}
上放置一个键(并在
{CompanyID,TypeOfLocation}
上放置一个键,以确保同一公司没有相同类型的多个位置),但这可能对于一个简单的“主要”要求来说过于复杂了。