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)
我真的无法弄清楚我做错了什么。
您已经确定了问题的原因。一种可能的解决方法是为链接列表创建另一个表:
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 会破坏列表。)