在多个外键中使用同一列

Using the same column in multiple foreign keys

我的问题与此非常相似:

我将使用该问题及其代码。答案中的代码正是我要找的解决方案,但是有错误。

一个学生可能会被列为一个讲座,也可能会被列为一个讲座组。如果删除讲座,则应删除所有学生列表及其所有组。如果一个组被删除,那么学生应该仍然被列为讲座,但他们不应该再被分配到一个组。

以上所有作品。但是,如果我尝试更改讲座 ID,我希望 studentListed 和组中的引用会相应更新(On Update Cascade),但由于某种原因,它在此外键约束上失败:

#1452 - Cannot add or update a child row: a foreign key constraint fails
(`SomeDB`.`studentListed`, CONSTRAINT `studentListed_ibfk_1` FOREIGN KEY (`lectureId`)
REFERENCES `lectures` (`lectureId`) ON DELETE CASCADE ON UPDATE CASCADE)

为什么会这样?原因似乎是 studentListed 中的 lectureId 既是对讲座中 lectureId 的引用,也是对组中 lectureId 的引用,后者本身就是一个引用。有更好的方法吗?

Sql 重现代码:

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;

INSERT INTO lectures
VALUES
(1, "lecture1");

INSERT INTO groups
VALUES
(1, 1, "group1");

INSERT INTO studentListed
VALUES
(1, 1, 1);

UPDATE lectures SET lectureId=2 WHERE lectureId=1; /* Offending line */

你不能更改为 lectureids 2 因为 none 存在于 table

的第一位

而级联以另一种方式工作

其他组 os mysql 中的保留字,应避免使用。

最后我不得不删除第二个外键,因为它已经存在于组中并且是不必要的,因为如果存在这样的 lectureid,组中组中的 fpoeign 键已经检查

外键工作示例

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;
INSERT INTO lectures
VALUES
(1, "lecture1");
INSERT INTO `groups`
VALUES
(1, 1, "group1");
INSERT INTO studentListed
VALUES
(1, 1, 1);
UPDATE lectures SET lectureId = 2 WHERE lectureId = 1
SELECT * FROM studentListed
studentId | lectureId | groupNo
--------: | --------: | ------:
        1 |         2 |       1
UPDATE lectures SET lectureId=2 WHERE lectureId=1; /* Offending line */
✓

✓

db<>fiddle here

你不能按照你的方式构建它,你必须从组中分解主键并只引用组,比如

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 PRIMARY KEY,
  title VARCHAR(10) NOT NULL,
  UNIQUE 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 (groupNo) REFERENCES `groups` (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;
INSERT INTO lectures
VALUES
(1, "lecture1");
INSERT INTO `groups`
VALUES
(1, 1, "group1");
INSERT INTO studentListed
VALUES
(1, 1, 1);
UPDATE lectures SET lectureId = 2 WHERE lectureId = 1
SELECT * FROM studentListed
studentId | lectureId | groupNo
--------: | --------: | ------:
        1 |         2 |       1
UPDATE lectures SET lectureId=2 WHERE lectureId=1; /* Offending line */
✓

✓
DELETE FROM lectures WHERE lectureID = 2
SELECT * FROM studentListed
studentId | lectureId | groupNo
--------: | --------: | ------:

db<>fiddle here

随着您的概念变得更加复杂,这还需要进一步改进