我正在用 Rails 编写一个新应用程序,所以我在每个表上都有一个 id 列。使用外键强制域约束的最佳实践是什么?我将概述我的想法和挫败感。
这就是我想象的“Rails Way”。这是我开始的。
Companies:
id: integer, serial
company_code: char, unique, not null
Invoices:
id: integer, serial
company_id: integer, not null
Products:
id: integer, serial
sku: char, unique, not null
company_id: integer, not null
LineItems:
id: integer, serial
invoice_id: integer, not null, references Invoices (id)
product_id: integer, not null, references Products (id)
这样做的问题是,来自一家公司的产品可能出现在另一家公司的发票上。我向 LineItems 添加了一个 (company_id: integer, not null),有点像我只使用自然键和序列号时所做的,然后添加了一个复合外键。
LineItems (product_id, company_id) references Products (id, company_id)
LineItems (invoice_id, company_id) references Invoices (id, company_id)
这将 LineItems 适当地限制为一家公司,但它似乎过度设计和错误。LineItems 中的 company_id 是无关的,因为代理外键在外表中已经是唯一的。Postgres 要求我为引用的属性添加一个唯一索引,因此我在产品和发票中的 (id, company_id) 上创建一个唯一索引,即使 id 只是唯一的。
下表包含自然键和序列发票编号不会增加这种复杂性,因为引用的列已经是自然键,因此它们已经具有唯一索引。
LineItems:
company_code: char, not null
sku: char, not null
invoice_id: integer, not null
我可以忽略 LineItems 表中的代理键,但这似乎也是错误的。当数据库已经有一个整数可供使用时,为什么要在 char 上加入数据库?此外,完全按照上述方式进行操作将需要我将 company_code(自然外键)添加到 Products 和 Invoices。
妥协...
LineItems:
company_id: integer, not null
sku: integer, not null
invoice_id: integer, not null
不需要其他表中的自然外键,但当有可用整数时,它仍然在 char 上加入。
有没有一种干净的方法可以像上帝预期的那样使用外键强制域约束,但是在代理存在的情况下,而不会将模式和索引变成复杂的混乱?