我想创建一个 MySQL 表来保存用户之间的关系数据。用户 A 与 B 和用户 B 与 A 之间的关系可以不同。
示例:
- (从)Bob(到)Alice 的关系:0.9 - Bob 喜欢 Alice 的东西。
- (从)爱丽丝(到)鲍勃的关系:0.5 - 爱丽丝认为鲍勃的东西平庸。
我的问题:
我在引用用户表中 user_id 的两个外键上实现了两个约束作为唯一键。我可以这样做吗?它们是否被视为两个独立的 UNIQUE KEY?
我如何实现一个 CONSTRAINT 只允许每个(从)UserA(到)UserB 关系和(从)UserB(到)UserA 关系每个 user_id 出现一次?我的做法是否正确?
SQL:
CREATE TABLE relationships (
relationship_id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT,
from_user_id MEDIUMINT UNSIGNED NOT NULL,
to_user_id MEDIUMINT UNSIGNED NOT NULL,
relationship_level DECIMAL(1,1) NOT NULL,
PRIMARY KEY (relationship_id),
FOREIGN KEY (from_user_id) REFERENCES users (user_id)
ON DELETE CASCADE ON UPDATE NO ACTION,
FOREIGN KEY (to_user_id) REFERENCES users (user_id)
ON DELETE CASCADE ON UPDATE NO ACTION,
CONSTRAINT from_to_relationship UNIQUE KEY (from_user_id, to_user_id),
CONSTRAINT to_from_relationship UNIQUE KEY (to_user_id, from_user_id),
INDEX relationship_from_to (relationship_id, from_user_id, to_user_id, relationship_level),
INDEX relationship_to_from (relationship_id, to_user_id, from_user_id, relationship_level)
) ENGINE=INNODB;
我希望有人能提供帮助。
最佳答案
只保留这些 UNIQUE
约束中的一个 - 没有必要同时拥有两者。当一行失败 UNIQUE KEY (from_user_id, to_user_id)
它也失败了 UNIQUE KEY (to_user_id, from_user_id)
反之亦然,所以它们在逻辑上是等价的。即使只有一个 UNIQUE
约束,当试图表示 Alice 和 Bob 之间的关系时,您最多可以有一个 {Alice, Bob} 行,和最多有一个 { Bob, Alice} 行。
至于性能(即双向遍历关系),您可能需要考虑索引 {from_user_id, to_user_id}
(用于“正向”遍历)和/或 {to_user_id, from_user_id}
(用于“向后”遍历)。您甚至可以放弃代理主键 (relationship_id
) 并选择自然 PK,从而降低索引的需要(二级索引对于聚簇表来说很昂贵,请参阅 Understanding InnoDB clustered indexes,“聚簇的缺点”部分").
在我看来,您的表格应该如下所示:
CREATE TABLE relationships (
from_user_id MEDIUMINT UNSIGNED NOT NULL,
to_user_id MEDIUMINT UNSIGNED NOT NULL,
relationship_level DECIMAL(1,1) NOT NULL,
PRIMARY KEY (from_user_id, to_user_id), -- InnoDB is clustered, so naturally "covers" relationship_level as well.
FOREIGN KEY (from_user_id) REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE NO ACTION,
FOREIGN KEY (to_user_id) REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE NO ACTION,
INDEX relationship_to_from (to_user_id, from_user_id, relationship_level) -- Including relationship_level may or may not be a good idea.
) ENGINE=INNODB;
注意:是否在 INDEX 中包含 relationship_level
取决于您是否需要 index-only scan在“向后”的方向也是如此。 “前进”方向自然被 PK 覆盖(因为 InnoDB 是 clustered )。
关于两个 UNIQUE KEY 上的 MySQL CONSTRAINT,它们使用相同的 FOREIGN KEY ID,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9249441/