aho*_*hoo 13 mysql cascade foreign-keys
假设我有两个下表:
CREATE TABLE post (
id bigint(20) NOT NULL AUTO_INCREMENT,
text text ,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1;
CREATE TABLE post_path (
ancestorid bigint(20) NOT NULL DEFAULT '0',
descendantid bigint(20) NOT NULL DEFAULT '0',
length int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (ancestorid,descendantid),
KEY descendantid (descendantid),
CONSTRAINT f_post_path_ibfk_1
FOREIGN KEY (ancestorid) REFERENCES post (id)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT f_post_path_ibfk_2
FOREIGN KEY (descendantid) REFERENCES post (id)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB;
Run Code Online (Sandbox Code Playgroud)
并插入这些行:
INSERT INTO
post (text)
VALUES ('a'); #// inserted row by id=1
INSERT INTO
post_path (ancestorid ,descendantid ,length)
VALUES (1, 1, 0);
Run Code Online (Sandbox Code Playgroud)
当我想更新帖子行ID时:
UPDATE post SET id = '10' WHERE post.id =1
Run Code Online (Sandbox Code Playgroud)
MySQL说:
#1452 - Cannot add or update a child row: a foreign key constraint fails (test.post_path, CONSTRAINT f_post_path_ibfk_2 FOREIGN KEY (descendantid) REFERENCES post (id) ON DELETE CASCADE ON UPDATE CASCADE)
Run Code Online (Sandbox Code Playgroud)
为什么?怎么了?
编辑:
当我插入这些行:
INSERT INTO
post (text)
VALUES ('b'); #// inserted row by id=2
INSERT INTO
post_path (ancestorid, descendantid, length)
VALUES (1, 2, 0);
Run Code Online (Sandbox Code Playgroud)
并更新:
UPDATE post SET id = '20' WHERE post.id =2
Run Code Online (Sandbox Code Playgroud)
Mysql成功更新了子行和父行.那么为什么我不能更新第一篇文章(id = 1)?
好吧,我通过我也可以访问的测试数据库运行您的模式和查询,并注意到以下内容; 将两行插入两个表之后,在任何更新之前,数据如下所示:
mysql> select * from post;
+----+------+
| id | text |
+----+------+
| 1 | a |
| 2 | b |
+----+------+
2 rows in set (0.00 sec)
mysql> select * from post_path;
+------------+--------------+--------+
| ancestorid | descendantid | length |
+------------+--------------+--------+
| 1 | 1 | 0 |
| 1 | 2 | 0 |
+------------+--------------+--------+
2 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
发出更新语句后,将post.id更新为20:
mysql> UPDATE `post` SET `id` = '20' WHERE `post`.`id` =2;
Query OK, 1 row affected (0.08 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from post_path;
+------------+--------------+--------+
| ancestorid | descendantid | length |
+------------+--------------+--------+
| 1 | 1 | 0 |
| 1 | 20 | 0 |
+------------+--------------+--------+
2 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
请注意,ancestorid仍为1,这似乎是MySQL的一个问题:
如果您使用涉及具有外键约束的InnoDB表的多表UPDATE语句,则MySQL优化器可能会按照与其父/子关系不同的顺序处理表.在这种情况下,语句失败并回滚.相反,更新单个表并依赖InnoDB提供的ON UPDATE功能,以便相应地修改其他表.请参见第14.3.5.4节"InnoDB和FOREIGN KEY约束".
你的第一个查询失败的原因是因为ancestorid没有更新到10,但是descendantid是,并且因为你试图将post.id设置为10,而post_path表中的ancestorid仍然引用值1,这将不会更长的存在.
您应该考虑更改模式以避免这种情况,并且还要避免更新auto_increment列以避免冲突.