mysql外键仅引用复合主键的一部分

Posted

技术标签:

【中文标题】mysql外键仅引用复合主键的一部分【英文标题】:mysql Foreign key referencing only part of composite primary key 【发布时间】:2014-09-04 12:04:37 【问题描述】:

当外键只引用复合主键的一部分时,如何从父表中删除??

我正在使用 mysql 5.6.2

我有一个具有主键 item(A,B) 的项目表和具有主键 itemsup(A,X,Y) 的项目供应商。我已经给出了参考,例如 item(A) = itemsup(A) 当我从项目(父)表中删除时,我收到错误:1451 - 无法删除或更新父行:外键约束失败

我的表结构如下。

CREATE TABLE IF NOT EXISTS ITEM ( /* parent */
  ITEMID INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ITEM ID',
  MCA ENUM('A','C','M','X') NOT NULL ,
  ITEMNAME VARCHAR(100) NOT NULL COMMENT 'NAME OF ITEM',
  PRIMARY KEY (ITEMID, MCA)
)
ENGINE = InnoDB;

CREATE TABLE IF NOT EXISTS ITEMSUP ( /* child */
  ITEMID INT UNSIGNED NOT NULL,
  SUPID INT UNSIGNED NOT NULL,
  MCA ENUM('A','C','M','X') NOT NULL ,
   PRIMARY KEY (ITEMID, SUPID, MCA),
  CONSTRAINT FK_ITEMSUP_ITEM1
    FOREIGN KEY (ITEMID)
    REFERENCES ITEM (ITEMID)
    ON DELETE NO ACTION 
    ON UPDATE CASCADE
 )
ENGINE = InnoDB;

INSERT INTO ITEM (ITEMID, MCA, ITEMNAME) VALUES ( 1,'A', 'ONE A');
INSERT INTO ITEM (ITEMID, MCA, ITEMNAME) VALUES ( 1,'M', 'ONE M');
INSERT INTO ITEMSUP(ITEMID, SUPID, MCA) VALUES(1,1,'X');
/* below is not working */
DELETE FROM ITEM WHERE ITEMID = 1 AND MCA ='A';

http://sqlfiddle.com/#!9/fb961/1

我找到了一种解决方案,但不确定它是对还是错。

SET FOREIGN_KEY_CHECKS = 0;
DELETE FROM ITEM WHERE ITEMID =1 AND MCA ='A';
SET FOREIGN_KEY_CHECKS = 1;

【问题讨论】:

为什么你的 FK 是 (ITEMID) 而不是 (ITEMID,MCA)? 【参考方案1】:

为什么会出现错误消息?

在标准 SQL 中,引用的列集必须声明为 UNIQUE 或 PRIMARY KEY。 MySQL 在引用 UNIQUE NOT NULL 或 PRIMARY KEY 时正常工作。

不幸的是,MySQL 允许引用的集合是非唯一的或允许 NULL 的唯一的,但随后不能正常工作。来自the MySQL manual re Using FOREIGN KEY Constraints:

但是,系统不会强制要求所引用的 列是 UNIQUE 或声明为 NOT NULL。外键的处理 对非唯一键或包含 NULL 值的键的引用不是 为 UPDATE 或 DELETE CASCADE 等操作定义良好。你是 建议使用仅引用 UNIQUE 的外键(包括 PRIMARY) 和 NOT NULL 键。

特别是from the MySQL manual re InnoDB and FOREIGN KEY Constraints:

如果父表中有几行具有相同的 引用的键值,InnoDB 在外键检查中就像 不存在具有相同键值的其他父行。例如, 如果您已经定义了 RESTRICT 类型约束,并且有一个孩子 具有多个父行的行,InnoDB 不允许删除 任何这些父行。

这就是您收到错误的原因。即,您在 ITEM 中有多个 ITEMID 行与同一行。

调整您的设计

就关系和标准 SQL 概念而言,如果 ITEMID 在 ITEM 中是唯一的,则只能在 ITEMSUP 中具有 FOREIGN KEY (ITEMID) REFERENCES ITEM (ITEMID)。由于 ITEMID 并不意味着您在 ITEM 中是唯一的,因此您不可能想要它的 FOREIGN KEY。 (通俗地说,ITEMID 不是 ITEM 的“键”,因此它不能是 ITEMSUP 中的外“键”。)

要让每个 ITEMSUP ITEMID 都成为 ITEM ITEMID 的值,请将 ITEMID PRIMARY KEY 设为新表,并从 ITEMID 和 ITEMSUP 获得一个 FOREIGN KEY(而不是当前的 ITEMSUP 外键)。

改进您的设计

也许“X”是一种“null”,表示没有 MCA,并且不一定有匹配的 MCS。和/或 ITEMSUP ITEMID 的 MCA 在非“X”和/或“X”时必须匹配其 ITEM MCA。也许您只希望 ITEM 中的每个 ITEMID 一个 MCA,在这种情况下,将 ITEM 更改为具有 ITEMID PRIMARY KEY 和 MCA NOT NULL UNIQUE。也许 ITEM 可以有多个 ITEMID-MCA 对,但 ITEMSUP 只能有那些,在这种情况下有不同的 ITEMSUP 外键:

FOREIGN KEY (ITEMID,MCA) REFERENCES ITEM (ITEMID,MCA)

但这会被您的 SQLFiddle 违反:

INSERT INTO ITEMSUP(ITEMID, SUPID, MCA) VALUES(1,1,'X');

因为ITEM中没有对应的子行(1,'X')。

如果任何“可能”为真,那么您没有正确描述表上的约束。除非您确切确认您需要的约束是什么,否则我们无法正确地建议您的设计应该是什么。

【讨论】:

以上是关于mysql外键仅引用复合主键的一部分的主要内容,如果未能解决你的问题,请参考以下文章

级联保存具有外键的实体对象作为复合主键的一部分

mysql复合外键引用超过2个属性

Spring用外键保存复合主键

主键和外键的复合键不能建立关系

如何为具有复合主键的表构建外键?

休眠仅保存具有复合主键中的父外键的子表条目