MYSQL - 同一个表上的外键问题

R. *_*rdi 6 foreign-key constraint mysql-5.5

在 Mysql 5.5 中,我有一个表,其中存储了一些有关“事件”的信息。为了按时间顺序排列,我创建了两列,分别引用前一个事件和下一个事件。所以我做了两个外键,引用同一个表的主键。如果一行是按时间顺序排列的第一行,则前一个元素的值将为空。

所以,我的表是:

calendars
--------
calendar_id (PRIMARY KEY)
calendar_prev
calendar_next
other columns...
Run Code Online (Sandbox Code Playgroud)

我有这个数据(作为例子)

+--------------+---------------+---------------+
|  calendar_id | calendar_prev | calendar_next |
+--------------+---------------+---------------+
|       1      |    NULL       |       2       |
|       2      |      1        |       3       |
|       3      |      2        |       4       |
|       4      |      3        |       5       |
|       5      |      4        |     NULL      |
+--------------+---------------+---------------+
Run Code Online (Sandbox Code Playgroud)

和这些外键。

ALTER TABLE `calendars`
ADD CONSTRAINT `calendars_ibfk_3` FOREIGN KEY (`calendar_next`) REFERENCES `calendars` (`calendar_id`) ON DELETE SET NULL ON UPDATE CASCADE,
ADD CONSTRAINT `calendars_ibfk_2` FOREIGN KEY (`calendar_prev`) REFERENCES `calendars` (`calendar_id`) ON DELETE SET NULL ON UPDATE CASCADE;
Run Code Online (Sandbox Code Playgroud)

这在我的服务器和 sqlfiddle 上运行良好。

当我尝试修改元素的 ID 时,问题就出现了。如果我没有遗漏一些关于外键如何工作的信息,如果我编辑 calendar_id 5 并将其设置为 7,例如,calendar_id = 4 的行也应该将 calendar_next 值从 5 更改为 7。但是我只是得到一个错误。换句话说,如果我尝试这个:

UPDATE `calendars` SET `calendar_id` = 7 WHERE `calendar_id` = 5;
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

 Cannot delete or update a parent row: a foreign key constraint fails (`db_2_813f44`.`calendars`, CONSTRAINT `calendars_ibfk_3` FOREIGN KEY (`calendar_next`) REFERENCES `calendars` (`calendar_id`) ON DELETE SET NULL ON UPDATE CASCADE):
Run Code Online (Sandbox Code Playgroud)

我真的无法弄清楚我做错了什么。

ype*_*eᵀᴹ 3

您已经确定了问题的原因。一种可能的解决方法是为链接列表创建另一个表:

CREATE TABLE calendars
  ( calendar_id INT PRIMARY KEY
  -- other columns
  ) ;

CREATE TABLE calendar_list
  ( calendar_id INT NOT NULL
  , calendar_next INT NOT NULL
  , PRIMARY KEY (calendar_id)
  , UNIQUE (calendar_next)
  , CONSTRAINT calendar_list_1
      FOREIGN KEY (calendar_id) 
      REFERENCES calendars (calendar_id) 
      ON DELETE CASCADE ON UPDATE CASCADE
  , CONSTRAINT calendar_list_2
      FOREIGN KEY (calendar_next) 
      REFERENCES calendars (calendar_id) 
      ON DELETE CASCADE ON UPDATE CASCADE
  ) ;
Run Code Online (Sandbox Code Playgroud)

然后可以使用以下查询来显示您现在拥有的内容:

SELECT c.calendar_id, 
       prev.calendar_id AS calendar_prev, 
       next.calendar_next
FROM calendars AS c
  LEFT JOIN calendar_list AS prev
    ON c.calendar_id = prev.calendar_next
  LEFT JOIN calendar_list AS next
    ON c.calendar_id = next.calendar_id ;
Run Code Online (Sandbox Code Playgroud)

UPDATE使用此设置将可以正常工作。在SQL-Fiddle进行测试。(删除是一个不同的问题,但以前的设计也没有解决这个问题。如果删除位于第一个或最后一个点之外的任何位置,则设置为 null 会破坏列表。)