【问题标题】:Linking 2 ID's in MySQL在 MySQL 中链接 2 个 ID
【发布时间】:2012-02-24 03:40:39
【问题描述】:

我有这张桌子

CREATE TABLE IF NOT EXISTS `links` (
  `link_id` int(20) NOT NULL AUTO_INCREMENT,
  `item1_id` int(20) NOT NULL,
  `item2_id` int(20) NOT NULL,
  PRIMARY KEY (`link_id`),
  UNIQUE KEY `item_id` (`item1_id`,`item2_id`)
) ENGINE=InnoDB;

如何限制它,使the item_id 只能在item1_iditem2_id 中出现一次

因为我希望一个项目只链接到另一个项目..

【问题讨论】:

    标签: php mysql mysql5


    【解决方案1】:

    您可以将UNIQUE KEY 缩小为只有item1_id。这意味着该表定义了1:1 关系,而不是1:n 关系。此外,您可以删除 auto_increment 主键,这些“链接”表中不需要它:

    CREATE TABLE IF NOT EXISTS links (
       item1_id int(20) NOT NULL,
       item2_id int(20) NOT NULL,
      PRIMARY KEY (item1_id),
      FOREIGN KEY (item1_id)                 --- I assume you have these 2
        REFERENCES item (item_id),           --- Foreign Keys, too
      FOREIGN KEY (item2_id)
        REFERENCES item (item_id)
    ) ENGINE=InnoDB;
    

    this 和@gbn 的答案的区别在于,这不允许 Nulls,并且不需要任何东西来存储未链接的项目。两种设计的工作方式几乎相同,只是对 Insert/Delete/Update 语句稍作修改。

    不过,在这两种设计中,我们都可以有如下链接的情侣:(1 -> 2)(2 -> 3)(3 -> 7)。如果这符合所需的规范,那么两种设计都可以。


    但是,如果我们只希望项目只出现在一对链接的对中,那么在链接的任一侧,实现起来就更难了。

    一种方法是确保links 表中的所有插入都是通过插入(1, 2)(2, 1) 对或失败的过程完成的(对于必须处理的删除/更新语句也是如此有 2 行)。

    其他更复杂的方式涉及触发器(或奇异结构,如索引视图,在 MySQL 中不可用)。

    如果你想要一个规范化的设计,还有这种方法(复杂但没有触发器):

    CREATE TABLE IF NOT EXISTS link_help (
       item1_id int(20) NOT NULL,
       item2_id int(20) NOT NULL,
      PRIMARY KEY (item1_id),
      FOREIGN KEY (item1_id) 
        REFERENCES item (item_id),  
      FOREIGN KEY (item2_id)
        REFERENCES item (item_id),
      UNIQUE KEY (item1_id, item2_id)          --- this will be needed below
    ) ENGINE=InnoDB;
    
    CREATE TABLE IF NOT EXISTS links (
       item1_id int(20) NOT NULL,
       item2_id int(20) NOT NULL,
      PRIMARY KEY (item1_id),
      FOREIGN KEY (item1_id, item2_id) 
        REFERENCES link_help (item1_id, item2_id), 
      FOREIGN KEY (item2_id, item1_id)               --- notice the different 
        REFERENCES link_help (item1_id, item2_id)    --- order here
    ) ENGINE=InnoDB;
    

    现在,您无法在links 表中添加(1 -> 2)(2 -> 3)(3 -> 7) 行。

    【讨论】:

    • 如果 item1 和 item2 是两个单独的表,这将起作用。我在想他们的意图是它们都是同一个表“项目”。在这种情况下,item.id 可以在 item1_id 或 item2_id 外键字段中。但是,如果不是并且它们是 2 个单独的项目表,则您的解决方案是正确的。
    • @Ray:当两个 item_id 都是同一个表的外键时,它应该可以正常工作。为什么不呢?
    【解决方案2】:

    我希望一个项目只链接到另一个项目

    这意味着您不需要链接表。您只需要在 Item 表中添加一个带有唯一约束的 linkedItemId 列。一旦 Item2 链接到 Item1(Item1ID 位于 Item2 行的 linkedItemId 中),则没有其他内容可以链接到 Item1。

    另外,链接表不需要自己的代理键

    编辑,请注意 MySQL 允许在唯一索引中使用多个 NULL(不像 SQL Server,您可以使用过滤的唯一索引来忽略 NULL)

    来自 MySQL 5.5 CREATE INDEX

    对于所有引擎,UNIQUE 索引允许包含 NULL 的列有多个 NULL 值

    【讨论】:

    • 试试看 - 这意味着您只能添加链接已知的记录(或者是一个虚拟条目) - 每条记录都必须相互链接(或唯一的无意义值)
    • 我同意第一条评论,即这不是一个好的解决方案。此方法只有在每个项目都有一个链接项目时才有效。外键字段指向当前表的 id,如果设置为 'UNIQUE',则该字段中不能有多个项目为空值。
    • @Ray:MySQL 不允许多个 NULL 吗? "For all engines, a UNIQUE index permits multiple NULL values for columns that can contain NULL."(与 SQL Server 不同,Sybase 最多允许一个 NULL)
    • 我的错...我 mysql 将允许多个空值。请编辑您的答案,我将取消我的反对票(如果不对答案进行编辑,我不会撤回)。
    • 只有 SQL-Server 将 NULL 视为具有唯一约束的列中的值(并且不允许多个 Null)。
    【解决方案3】:

    ...还有一个问题是链接是否有明确的方向 - 即 A-B 与 B-A 是否不同?

    认为(您需要对此进行测试)您可以使用触发器来处理这两种情况。但是,我不知道有任何方法可以使用 MySQL 的过程语言显式抛出错误。由于您已指定列不应为无默认值的 NOT NULL,并假设 NEW 是可写的,那么....

    CREATE TRIGGER ins_link BEFORE INSERT on links
    FOR EACH ROW
    BEGIN
      IF (NEW.item1_id = NEW.item2_id) THEN
          NEW.item2_id=NULL; /* subsequent INSERT will fail */
      END IF
      /* if you want AB=BA.... */
      IF (NEW.item1_id > NEW.item2_id) THEN
         @tempvar=NEW.item1_id;
         NEW.item1_id=NEW.item2_id;
         NEW.item2_id=@tempvar;
      END IF;
    END;
    

    (您可能也需要更新前触发器,但其余代码相同)。

    【讨论】:

    • 我的回答也不会停止 1->3, 3->2, 2->1。我还假设方向在链接表中并不重要。使用其他 RDBMS 中的计算列或使用有效的 CHECK 约束更容易......
    【解决方案4】:

    我不认为 INNODB 可以强制执行这样的约束(或 MyIsam)。我的建议是创建一个存储过程来处理插入。首先检查您的自定义约束需求,如果没有冲突,则照常插入。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-25
      • 2011-05-21
      • 1970-01-01
      • 2014-03-21
      • 2022-01-26
      • 1970-01-01
      • 2021-10-19
      • 1970-01-01
      相关资源
      最近更新 更多