我正在用Rails编写一个新应用程序,所以我在每个表上都有一个id列。使用外键实施域约束的最佳做法是什么?我将概述我的想法和挫败感。如何在存在代理键时为域约束定义复合外键?
下面是我想象的“The 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,不为null),就像我只会使用自然键和连续字符一样,然后添加一个组合外键。
LineItems (product_id, company_id) references Products (id, company_id)
LineItems (invoice_id, company_id) references Invoices (id, company_id)
这妥善限制了LineItem到一个单一的公司,但它似乎过度设计和错误的。 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
不需要其它表自然外键,但它仍然是在焦炭在接合时有可用的整数。
有没有干净的方式执行与外国键域约束像上帝打算,但在代理人的情况下,头也不回的模式和索引到一个复杂的烂摊子?