多列外键:将单列设置为 Null “ON DELETE”而不是全部

Posted

技术标签:

【中文标题】多列外键:将单列设置为 Null “ON DELETE”而不是全部【英文标题】:Multiple Column Foreign Key: Set single column to Null "ON DELETE" instead of all 【发布时间】:2016-02-17 13:04:24 【问题描述】:

常规:给定多个列的外键,其中一些可能为 NULL。 默认情况下 (MATCH SIMPLE) mysql/MariaDB InnoDB 只要多列外键的至少一列为 NULL,就不会检查外键。

要求:如果从父项中删除一行,则相应子项的一列应设置为 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
 );

【问题讨论】:

【参考方案1】:

经过一些研究,似乎使用外键无法实现该特定要求。

最好的解决方案似乎是混合使用外键触发器

可以通过以下语句解决给定示例的问题:

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;

请注意,最后一个外键的“ON DELETE CASCADE”永远不会导致级联删除,因为触发器已经通过将相应行设为空来删除外键引用。

补充:除了使用“ON DELETE CASCADE”,可以使用“ON DELETE SET NULL”和相同的触发器,但是“lectureId”必须可以为空,并且应该包含“CHECK(lectureId IS NOT NULL)" 以确保它永远不会设置为 null

【讨论】:

以上是关于多列外键:将单列设置为 Null “ON DELETE”而不是全部的主要内容,如果未能解决你的问题,请参考以下文章

mysql 从多列中选择值到单列中

pyspark将单列转换为多列[重复]

如何使用 Qt 将两个单列 csv 文件合并为一个多列 csv 文件? [关闭]

函数将多列作为单列而不是多列返回

Pandas DataFrame 将多列值堆叠到单列中

如何在雪花sql中将单列拆分为多列[重复]