两个UNIQUE KEY上的MySQL CONSTRAINT,使用相同的FOREIGN KEY ID

时间:2012-02-12 14:14:58

标签: mysql database-design constraints database-schema unique-key

我想创建一个MySQL表来保存用户之间的关系数据。用户A到B和用户B到A之间的关系可以不同。

示例:

  1. (来自)Bob(与)Alice的关系:0.9 - Bob喜欢Alice的东西。
  2. (来自)Alice(对)Bob的关系:0.5 - Alice发现Bob的东西平庸。
  3. 我的问题:

    我在两个引用用户表中的user_ids的FOREIGN KEYS上实现了两个CONSTRAINTS作为UNIQUE KEY。我可以这样做吗?它们被视为两个独立的唯一键吗?

    如何实现一个CONSTRAINT只允许每个user_id发生一次(from)UserA(to)UserB关系和(from)UserB(to)UserA关系?我是以正确的方式去做的吗?

    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;
    

    我希望有人可以提供帮助。

1 个答案:

答案 0 :(得分:2)

只保留其中一个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)。