如何存储“主要”记录

时间:2013-12-10 20:52:57

标签: database database-design

假设我有以下表格

Companies
--CompanyID
--CompanyName

Locations
--LocationID
--CompanyID
--LocationName

每家公司至少有一个地点。我想跟踪每家公司的primary位置(是的,每家公司都只有一个主要位置)。设置它的最佳方法是什么?在primaryLocationID表中添加Companies

3 个答案:

答案 0 :(得分:2)

  

在Companies表中添加primaryLocationID?

是的,但是这会创建一个循环引用,可能会阻止您插入新数据:

enter image description here

解决这个鸡蛋问题的一种方法是简单地让Company.PrimaryLocationID为空,这样你就可以暂时禁用其中一个圆形FK。遗憾的是,数据库只会强制执行“1:0..1”,而不是严格的“1:1”关系(因此您必须在应用程序代码中强制执行)。

但是,如果您的DBMS支持延迟约束(例如Oracle或PostgreSQL),您可以简单地推迟其中一个FK以在事务仍在进行时中断循环。在交易结束时,两个FK都必须到位,从而产生真正的“1:1”关系。


另一种解决方案是在Locations表中为主要位置设置一个标志,并在非主要位置设置NULL(注意U1,表示UNIQUE约束,确保公司不能有多个主要位置):

enter image description here

CREATE TABLE Location (
    LocationID INT PRIMARY KEY,
    CompanyID INT NOT NULL, -- References Company table, not shown here.
    LocationName VARCHAR(50) NOT NULL, -- Possibly UNIQUE?
    IsPrimary INT CHECK (IsPrimary IS NULL OR IsPrimary = 1), -- Use a BIT or BOOLEAN if supported by your DBMS.
    CONSTRAINT Locations_U1 UNIQUE (CompanyID, IsPrimary)
);

不幸的是,这有一些问题:

  • 即使在支持延迟约束的DBMS上,它也只能保证“1:0..1”(但不是真正的“1:1”)。
  • 它需要一个额外的索引(支持UNIQUE约束)。每个索引都会带来一定的开销,主要用于INSERT / UPDATE / DELETE性能。此外,clustered tables中的二级索引包含PK的副本,这可能使它们比预期的“更胖”。
  • 它依赖于符合ANSI标准的复合UNIQUE约束,如果任何(但不一定是所有)字段为NULL,则允许重复行。不幸的是,并非所有DBMS都遵循该标准,因此上述内容在Oracle或MS SQL Server下不会开箱即用(但可以在PostgreSQL和MySQL下运行)。您可以使用filtered唯一索引而不是UNIQUE约束来解决该问题,但并非所有DBMS都支持该索引。

BaBL86's solution型号M:N,而您的要求似乎是1:N。尽管如此,通过在{LocationID}(和{CompanyID, TypeOfLocation}上放置一个密钥来确保同一公司不能有多个相同类型的位置),可以将该模型“强制”为1:N,但是可能过度设计了一个简单的“主要”要求。

答案 1 :(得分:1)

我认为您自己的解决方案是最好的 - 这可以确保每家公司只能拥有一个主要位置。通过将其设为NOT NULL列,您甚至可以强制执行每个公司都应该拥有主要位置。

使用BaBL86's solution,您没有这些限制:公司可以拥有0 - 无限制的“主要位置”,这显然是不可能的。

请注意,如果您使用外键约束并将primaryLocationID定义为NOT NULL列,则会遇到问题,因为您基本上有一个循环(位置指向公司,公司指向位置)。您无法创建新公司(因为它需要主要位置),也无法创建新位置(因为它需要公司)。

答案 2 :(得分:-1)

我使用数据透视表:

CompanyLocations
    --CompanyID
    --LocationID
    --TypeOfLocation (primary, office, warehouse etc.)

在这种情况下,您可以根据需要选择所有位置,而不是使用类型。如果您创建PrimaryLocationID - 您需要一个表的两个连接和更复杂的逻辑。这比这还糟糕。