【问题标题】:Many-to-many relationship with compound primary key constraint与复合主键约束的多对多关系
【发布时间】:2015-08-19 03:38:39
【问题描述】:

我需要在 DBMS 中使用其他约束进行多对多关系设计。

有两个表,t1t2,其中 t1.idt2.id 分别是主键。 t1t2 是多对多的关系,所以很自然的设计是添加第三张表t3 与外键。例如,

t3:
id
t1_id     (foreign key refereed to t1.id)
t2_id     (foreign key refereed to t2.id)

另一个要求pair(t1.id, t2.id)应该与t4.id具有一对一关系。用primary_key(t1.id, t2.id)t5 好不好?或者我们可以直接使用t3's t1_id, t2_id 作为复合主键?我们需要快速扫描pair(t1.id, t2.id)t4中是否有实体,即pair(t1.id, t2.id)t4.id是一一对应的关系。

如果您能给我一些提示,不胜感激。

【问题讨论】:

  • 不需要加t5。你可以在t3 中设置(t1_id,t2_id) UNIQUE。您实际上可以将t3t4 之间的外键引用存储在t3 中。只需使t4_id fk 列在t3 中唯一。如果你想让引用变成另一种方式,你也可以将t3_id fk 列存储在t4 中。使用复合键作为外键是有效的,但对于使用代理 id 列模式的团队来说,他们倾向于保持这种模式。代理键的优点之一是它避免了实体表的复合外键。

标签: mysql foreign-keys many-to-many


【解决方案1】:

如果您必须在 (t1.id,t2.id)t4.id 之间强制执行一对一的关系,这似乎表明 (t1.id,t2.id) 将是唯一的。

如果是这种情况,如果 (t1.id,t2.id)t3 中应该是唯一的,那么您可以将 PRIMARY KEY 设为 t3

您可以添加一个对 t4 的外键引用,并使其成为唯一的,这样 t3 中的一行不能与 t4相关联>。或者,你可以让外键引用另一种方式,将t3的主键值存储在t4中。

例如,使t3中的外键为t4

 CREATE TABLE t3
 ( t1_id  INT NOT NULL       COMMENT 'pk, fk ref t1'
 , t2_id  INT NOT NULL       COMMENT 'pk, fk ref t2'
 , t4_id  INT                COMMENT 'fk ref t4'
 , PRIMARY KEY (t1_id,t2_id)
 , CONSTRAINT t3_ux2 UNIQUE KEY (t4_id)
 , CONSTRAINT fk_t3_t1 FOREIGN KEY (t1_id) REFERENCES t1(id)
     ON DELETE CASCADE ON UPDATE CASCADE
 , CONSTRAINT fk_t3_t2 FOREIGN KEY (t2_id) REFERENCES t2(id)
     ON DELETE CASCADE ON UPDATE CASCADE
 , CONSTRAINT fk_t3_t4 FOREIGN KEY (t4_id) REFERENCES t4(id)
     ON DELETE RESTRICT ON UPDATE CASCADE
 ) ...

或者,您可以在 t3 上引入代理主键。 (我们通常只在有其他表具有对 t3 的外键引用时才这样做;因为现在 t3 更像是一个实际实体,而不是纯粹的关系。这避免了我们必须使用复合键作为另一个表中的外键来引用 t3

例如:

 CREATE TABLE t3
 ( id     INT NOT NULL       COMMENT 'pk'
 , t1_id  INT NOT NULL       COMMENT 'fk ref t1'
 , t2_id  INT NOT NULL       COMMENT 'fk ref t2'
 , PRIMARY KEY (t3_id)
 , CONSTRAINT t3_ux1 UNIQUE KEY (t1_id,t2_id)
 , CONSTRAINT fk_t3_t1 FOREIGN KEY (t1_id) REFERENCES t1(id) 
     ON DELETE CASCADE ON UPDATE CASCADE
 , CONSTRAINT fk_t3_t2 FOREIGN KEY (t2_id) REFERENCES t2(id) 
     ON DELETE CASCADE ON UPDATE CASCADE
 ) ...

您可以在此表 t3 中添加一个外键来引用 t4,如上例所示。

或者您可以实现从 t4 到引用 t3 的关系。

任何一种方式都可以。

我的决定主要基于

1) 避免复合键作为外键(如果没有实体表有复合键)

2) 我们是否想要或需要ON DELETE CASCADE 功能,需要以何种方式工作...从 t3 删除应该级联删除到 t4,或相反。

我在示例中显示了ON DELETE RESTRICT,将t4_id fk 列添加到t3。我认为从 t4 中删除可能不应该通过从 t3 中删除行来“破坏” t1t2 之间的关系kbd>.

【讨论】:

  • 非常感谢您的详细解释。我更喜欢建议的代理主键,因为更进一步的情况是 (t1.id, t2.id) 与 t6.id 多对多,依此类推。
  • @frocdcn:在我提出建议之前,我通常希望对我们为业务建模的实体和关系有更多的了解......“客户”、“订单”、“发货”、 “付款”等,所以我可以提出合理的问题,这样我们就可以提出一个实体关系模型来反映我们需要表示的内容。但是...不知道这些,如果t3 中的行与其他实体有关系,我会选择t3 上的代理id 主键列。如果t3 上还有其他属性,我还会添加id PK,它看起来更像一个实体。
  • 我非常同意我们正在为业务建模。这里的核心问题是 (t1.id, t2.id) 必须用作单个属性。 (t1.id, t2.id) 与其他表有复杂的关系,因此代理键对于整个业务来说会更容易。然而我对代理键的性能补偿有些怀疑,因为它必须迭代 t3 中的每个元组以在 t3 中查找特定的 t1.id。我认为如果我们在编写 t3.id 时使用某种算法,很可能会从 t3.id 得到 t1.id 或 t2.id,这样就不会是自增 PK。
  • 拥有适当的可用索引将使 MySQL 能够以最佳方式执行所需的搜索。 JOIN 操作可以非常有效。我不会担心“为 t3 中的特定 t1.id 迭代 t3 中的每个元组”的性能(它不会是迭代的,除非我们通过使其迭代来阻碍 MySQL。如果你说可能会有性能将t1.id 存储在t4 中并避免连接操作的优势,在某些情况下可能是正确的。如果您关心查找以获取 t3.id,这也可能是正确的。但是对于整数类型和索引,不太可能是性能问题。
  • 你有ORM映射的经验/建议吗?比如休眠。我是 ORM 的新手,所以我不确定 Hibernate 是否可以处理不断变化的表。
猜你喜欢
  • 2020-01-08
  • 2013-03-12
  • 1970-01-01
  • 2021-02-03
  • 1970-01-01
  • 1970-01-01
  • 2015-12-13
  • 2020-05-30
  • 1970-01-01
相关资源
最近更新 更多