ruby-on-rails - 在代理键存在的情况下,我应该如何为域约束定义复合外键?

标签 ruby-on-rails postgresql foreign-keys composite-key surrogate-key

我正在使用 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 上连接。

有没有一种干净的方法可以像上帝所希望的那样使用外键强制域约束,但在代理人存在的情况下,又不会将架构和索引变成一团糟?

最佳答案

您在上面提到 id 已经是唯一的,但您必须使 id、company_id 唯一,因为您在 FK 引用中使用它需要确保它只引用 1 个唯一项目,而不是任何其他项目。

我知道您想“保护”数据,但我会说这是过度设计并同意您的看法。任何使用此数据但无法正常工作(为公司提取正确产品)的系统都会立即引起注意。这将在编程时带来处理更多数据的负担,并可能使事情变得更加困惑。

我喜欢保护数据的努力,但随着您获得更多数据,成本可能会变得很高,而且更复杂的数据模型肯定会增加负担。

我认为通过在应用程序中“自然”使用,数据得到了足够的保护。当您创建新发票并寻找要放在发票上的产品时,您将使用与创建发票相同的公司 ID 来搜索产品。如果您以某种方式在其中获取了错误的 ID(糟糕的代码,直接在数据库中的用户),那么糟糕的结果将立即引起注意。

我不太明白你帖子的第二部分。看起来您正试图走一条复杂的道路,而不是创建一个简单且规范化的模式。

为什么您如此担心输入无意义的键?您会让用户以非标准方式访问这些数据吗?

关于ruby-on-rails - 在代理键存在的情况下,我应该如何为域约束定义复合外键?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2553701/

相关文章:

ruby-on-rails - 上传的视频和streamio-ffmpeg

ruby-on-rails - Rails 中的 Postgres 查询 : where not

c# - 没有子导航属性的 EF 一对多外键

php - Laravel 5.6 与 whereHas 的多态关系

javascript - rails 5 : JS files not loading in Production

ruby-on-rails - 我是否需要额外的 gem 或可能需要运行以前版本的 gem?

ruby-on-rails - Rails 在创建时向表中添加数据

python - SQLAlchemy 为 postgres JSON 列设置默认值

sql - 按时间间隔对事件进行分组和计数,以及运行总计

php - MySql - 避免使用联结和外键的 SET()