在多个外键中使用同一列
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
随着您的概念变得更加复杂,这还需要进一步改进
我的问题与此非常相似:
我将使用该问题及其代码。答案中的代码正是我要找的解决方案,但是有错误。
一个学生可能会被列为一个讲座,也可能会被列为一个讲座组。如果删除讲座,则应删除所有学生列表及其所有组。如果一个组被删除,那么学生应该仍然被列为讲座,但他们不应该再被分配到一个组。
以上所有作品。但是,如果我尝试更改讲座 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
随着您的概念变得更加复杂,这还需要进一步改进