【问题标题】:Using the same column in multiple foreign keys在多个外键中使用同一列
【发布时间】:2021-12-04 00:31:10
【问题描述】:

我的问题与此非常相似:Multiple Column Foreign Key: Set single column to Null "ON DELETE" instead of all

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

可能会列出一个学生参加讲座,也可以选择参加其中一个讲座组。如果讲座被删除,所有学生列表及其所有组都应该被删除。如果一个小组被删除,那么学生应该仍然被列出来参加讲座,但他们不应该再被分配到一个小组中。

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

#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 */

【问题讨论】:

  • 请把提出这个问题所需的一切都放在这篇文章中。只提供您需要的东西并将其与您的问题联系起来。

标签: mysql sql foreign-keys


【解决方案1】:

您不能更改为 Lectureids 2,因为表格中首先不存在任何内容

级联以另一种方式工作

Further GROUPS 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
学生号 |讲座ID |组号 --------: | --------: | ------: 1 | 2 | 1
UPDATE lectures SET lectureId=2 WHERE lectureId=1; /* Offending line */
✓ ✓

db小提琴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
学生号 |讲座ID |组号 --------: | --------: | ------: 1 | 2 | 1
UPDATE lectures SET lectureId=2 WHERE lectureId=1; /* Offending line */
✓ ✓
DELETE FROM lectures WHERE lectureID = 2
SELECT * FROM studentListed
学生号 |讲座ID |组号 --------: | --------: | ------:

db小提琴here

随着您的概念越来越复杂,这将需要更多改进

【讨论】:

  • 删除第一个外键的问题是 studentListed LectureId 不会与 Lecture 级联,除非学生在一个组中。例如,如果您删除一个讲座,那么它不会删除相应的 studentListed 条目。
  • 您不能将两个外键都放在该表中。每次更改都会违反外键或其他。
  • @John 你的逻辑也是有缺陷的,因为 irt 会先删除组,然后删除 studentListed,请参阅dbfiddle.uk/… 如果你想包含组,可以用你的方式解决组的问题,然后它可以' t 在外键关系中
  • 尝试在不插入任何组的情况下删除学生正在进行的讲座。您还可以为不存在的讲座插入学生。 dbfiddle.uk/…
  • 请仔细阅读我的评论并查看第二个示例如何解决这个问题,与组合主键组合的组根本不起作用,你也永远不会让它起作用
猜你喜欢
  • 1970-01-01
  • 2021-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-24
  • 2021-04-07
  • 2016-01-07
相关资源
最近更新 更多