【问题标题】:Unique constraints on their own or as a primary key?自己的唯一约束还是作为主键?
【发布时间】:2019-11-12 21:08:21
【问题描述】:

使用这样的表架构有什么好处:

CREATE TABLE review (
    review_id SERIAL PRIMARY KEY,
    account_id INT REFERENCES account(account_id) NOT NULL, 
    product_id INT REFERENCES product(product_id) NOT NULL, 
    rating SMALLINT NOT NULL, 
    comment TEXT,
    UNIQUE (account_id, product_id)
);

或者约束本身应该是主键,像这样:

CREATE TABLE review (
    CONSTRAINT review_pkey (account_id, product_id) PRIMARY KEY,
    account_id INT REFERENCES account(account_id) NOT NULL, 
    product_id INT REFERENCES product(product_id) NOT NULL, 
    rating SMALLINT NOT NULL, 
    comment TEXT,
);

【问题讨论】:

  • 老派与新派。
  • @MatthewLerner 。 . .我更喜欢第一种方法,因为我喜欢有一个单列主键。话虽如此,各有优缺点,很难说哪个更好。
  • @GordonLinoff 我也喜欢第一种方法,但我想知道除了浪费的磁盘空间之外,它是否有任何不利之处,除了我的感觉之外是否还有任何好处。只是想看看我在数据库设计方面更有经验的人所缺少的东西。

标签: sql postgresql database-design natural-key


【解决方案1】:

第二个版本显然更可取,因为它需要少一列和少一索引,而且没有缺点。

列很明显,索引不是,因为您忘记添加它们: 您需要所有外键列的索引,以便可以快速删除引用的表。使用人工主键,您需要review_idaccount_idproduct_id 上的索引,而没有您可以使用(account_id, product_id)product_id 上的索引。

唯一支持第一个解决方案的人是那些坚信每张表都必须有一个人工生成的数字主键的人,无论如何。实际上,从引用的表中人工生成的两个键的组合也一样好。

【讨论】:

  • 总的来说,我可以部分同意这个说法。但是,所有表上的id primary key 可能具有相当实用的方面。例如,这使得在应用程序中轻松自动生成表单成为可能。这与宗教无关。
  • 没错,如果您选择限制您的工具,您将受到限制。
  • 当您创建工具时,简单性可能比多余一栏更重要。这不是一个限制,而是一个实际的选择。
  • 很抱歉,这似乎是题外话,但是有什么理由你只在(account_id, product_id)product_id而不是account_id上放置索引?
  • @MatthewLerner:这种索引策略是有充分理由的。见:dba.stackexchange.com/q/27481/3684
【解决方案2】:

除了宗教、习惯、个人偏好和某些客户端工具的便利性之外,还有其他很好的理由需要额外的代理 PK,如您的第一个示例所示。

如果您要使用其他表中的外键引用该表:

  • 引用表只需要包含单个代理 PK 列作为 FK 引用,这样更小、更快、更简单。如果引用表有很多行而review 没有,则单个实例可能已经超过了review 的额外成本。否则,可能会有多个实例。
    对于在 许多 行中引用的小型查找表,甚至可以考虑使用 smallserial 代理 PK - 如果 实际上 有帮助的话。见:

  • 通常,在引用表的 FK 列上也会有一个索引。您的带有两个 integer 的示例最适合多列 PK / FK,因为它将索引大小保持在最低限度。两个integer 列上的B 树索引不大于单个integer 上的一个(8 字节通常是索引元组的最小“有效负载”)。其他更大的数据类型会产生额外的影响。

  • 如果review 收到对(account_id, product_id) 列之一的许多更新,则这些更新将级联到基于这两列的所有引用表。成倍增加写入成本,使多个表和索引膨胀。如果它级联到宽行或许多引用行,则成本可能会大幅增加。所有这些都可以通过代理 PK 来避免 - 如果关系设计实际上应该以这种方式工作。

如果review 涉及到许多使用连接的查询,连接两列而不是一列会更繁琐且成本略高。同样,对于更大的数据类型更是如此。

也就是说,如果您没有上述(或类似),请查看 Laurenz 的回答。

权衡实际成本,而不是宗教信仰。

【讨论】:

  • 好吧,我应该省略宗教。如果您引用复合主键,我认为您需要的两列会浪费一些空间,但我看不出它会如何减慢处理速度。你可以解释吗?另外,我不认为引用映射表的外键很常见。如果你更新主键,你就做错了,所以我不相信这个论点。我主要关心的不是附加列,而是附加索引。
  • 不一定是“映射表”,也不必声称它很常见。 If you update primary keys, you are doing something terribly wrong. 大多如此。所以在这种情况下不要制作两列PK。这不是我的观点吗?额外的索引一个问题,没有争论。
  • 我同意。我误解了您所说的关于更新其中一个 ID 的内容:我认为您的意思是从一个引用表中的主键更改级联的更新,但不一定是这样 - 可能是这样(仍然假设它是映射表)有人更新映射以更改与之关联的对象。可能也不经常发生。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-25
  • 2011-05-28
相关资源
最近更新 更多