M.S*_*.S. 6 mysql sql innodb foreign-keys mariadb
一般:给定多个列的外键,其中一些可能为 NULL。
默认情况下(MATCH SIMPLE)只要多列外键中至少有一列为 NULL,MySQL/MariaDB InnoDB 就不会检查外键。
要求:如果从父级删除一行,则相应子级的一列应设置为 NULL,但外键中的两列不应设置为 NULL。
示例/描述:学生可能会被列入某个讲座的名单,也可能会被列入其中一个讲座小组的名单。如果讲座被删除,所有学生列表(作品)及其所有组(作品)都应被删除。如果仅删除单个组,则学生仍应列出讲座,但不应再将他们分配到组(问题)。
示例/SQL:以下 SQL 说明了此示例,但最后一条语句不起作用,因为最后一个 FOREIGN KEY 要求 LectureId 和 groupId 都可为 NULL,但使两者都可为 NULL 将意味着删除组也会将 LectureId 设置为 NULL 。
CREATE TABLE lectures (
lectureId INT NOT NULL,
title VARCHAR(10) NOT NULL,
PRIMARY KEY (lectureId)
);
CREATE TABLE groups (
lectureId INT NOT NULL,
groupNo INT NOT NULL,
title VARCHAR(10) NOT NULL,
PRIMARY KEY (lectureId,groupNo),
FOREIGN KEY (lectureId) REFERENCES lectures (lectureId)
ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE studentListed (
studentId INT NOT NULL,
lectureId INT NOT NULL,
groupNo INT NULL,
PRIMARY KEY (studentId,lectureId),
FOREIGN KEY (lectureId) REFERENCES lectures (lectureId)
ON UPDATE CASCADE ON DELETE CASCADE,
FOREIGN KEY (lectureId,groupNo) REFERENCES groups (lectureId,groupNo)
ON UPDATE CASCADE ON DELETE SET NULL
);
Run Code Online (Sandbox Code Playgroud)
经过一些研究后,似乎使用外键无法实现该特定要求。
最好的解决方案似乎是混合使用外键和触发器。
对于给定的示例,可以通过以下语句解决该问题:
CREATE TABLE lectures (
lectureId INT NOT NULL,
title VARCHAR(10) NOT NULL,
PRIMARY KEY (lectureId)
);
CREATE TABLE groups (
lectureId INT NOT NULL,
groupNo INT NOT NULL,
title VARCHAR(10) NOT NULL,
PRIMARY KEY (lectureId,groupNo),
FOREIGN KEY (lectureId) REFERENCES lectures (lectureId)
ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE studentListed (
studentId INT NOT NULL,
lectureId INT NOT NULL,
groupNo INT NULL,
PRIMARY KEY (studentId,lectureId),
FOREIGN KEY (lectureId) REFERENCES lectures (lectureId)
ON UPDATE CASCADE ON DELETE CASCADE,
FOREIGN KEY (lectureId,groupNo) REFERENCES groups (lectureId,groupNo)
ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TRIGGER GroupDelete BEFORE DELETE ON groups
FOR EACH ROW
UPDATE studentListed SET studentListed.groupNo = NULL
WHERE studentListed.lectureId = OLD.lectureId
AND studentListed.groupNo = OLD.groupNo;
Run Code Online (Sandbox Code Playgroud)
请注意,最后一个外键的“ON DELETE CASCADE”永远不会导致级联删除,因为触发器已经通过清空相应的行来删除外键引用。
添加:不使用“ON DELETE CASCADE”,可以使用具有相同触发器的“ON DELETE SET NULL”,但“lectureId”必须可为空,并且应该包含“CHECK(lectureId IS NOT NULL)”以确保它永远不会设置为 null