MySQL (Percona) 错误 1452:无法无故添加或更新子行
MySQL (Percona) Error 1452: Cannot add or update a child row for no reason
我有 2 个数据库表“用户”和“任务”。
任务 table 包含 createdBy_user_id
和 updatedBy_user_id
列的两个外键,它们都引用 users.id
。如果我尝试向 tasks
插入一个条目(在确保外键引用的用户存在之后),如下所示:
INSERT INTO tasks (createdBy_user_id,updatedBy_user_id,noOfYear,createdAt,updatedAt,status,customer_id)
VALUES (1,1,1,NOW(),NOW(),"open",1)
查询失败,错误 1452:
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test`.`tasks`, CONSTRAINT `user_id_fk_constr` FOREIGN KEY (`createdBy_user_id`) REFERENCES `users` (`id`))
我不知道为什么会这样,因为如果我删除约束,一切都会正常。 “updatedBy_user_id”列不会发生同样的错误,这让这让人感到困惑。
这些表具有以下 DDL:
用户table:
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`active` tinyint(1) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`email_confirmed_at` datetime DEFAULT NULL,
`username` varchar(50) NOT NULL,
`password` varchar(255) NOT NULL,
`first_name` varchar(50) DEFAULT NULL,
`last_name` varchar(50) DEFAULT NULL,
`job` varchar(64) DEFAULT NULL,
`position` varchar(64) DEFAULT NULL,
`specialKnowledge` text,
`tasks` text,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
任务table:
CREATE TABLE `tasks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`createdAt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updatedAt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`noOfYear` int(11) NOT NULL,
`year` int(11) GENERATED ALWAYS AS (right(year(`createdAt`),2)) VIRTUAL NOT NULL,
`createdBy_user_id` int(11) NOT NULL,
`updatedBy_user_id` int(11) NOT NULL,
`status` enum('open','closed') NOT NULL,
`customer_id` int(11) NOT NULL,
`projectDescription` text,
PRIMARY KEY (`id`),
UNIQUE KEY `tasks_year_unique_constr` (`year`,`noOfYear`),
KEY `user_id_fk_constr` (`createdBy_user_id`),
KEY `customer_id_fk_constr` (`customer_id`),
KEY `user_up_id_fk_constr` (`updatedBy_user_id`),
CONSTRAINT `customer_id_fk_constr` FOREIGN KEY (`customer_id`) REFERENCES `Customer` (`id`),
CONSTRAINT `user_id_fk_constr` FOREIGN KEY (`createdBy_user_id`) REFERENCES `users` (`id`),
CONSTRAINT `user_up_id_fk_constr` FOREIGN KEY (`updatedBy_user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4
如您所见,数据类型匹配并且 table 都使用 InnoDB Engine
。
用户 table 包含一项:
select id,username from users;
+----+----------+
| id | username |
+----+----------+
| 1 | admin |
+----+----------+
1 row in set (0.00 sec)
所以没有明显的原因,为什么插入会失败。你知道我的 table 有什么问题吗?看起来像是软件错误。
这是一个错误。 MySQL 5.7 在生成列和外键方面遇到了一些问题,我认为这是 Bug #79772 Foreign key not allowed when a virtual index exists.
的未修复变体
在您的特定情况下,可能取决于您的确切版本,以下任何修改似乎都可以防止该错误的发生:
- 不使用虚拟列,但使其成为
stored
- 不要为直接跟在虚拟列之后的列创建外键,例如将列顺序更改为
year, status, createdBy_user_id, updatedBy_user_id
.
- 不要在虚拟列上使用
unique
索引,普通索引应该没问题(至少在修复了链接错误的版本中)。您需要一个唯一的约束,所以这不是一个选项,但是这可以解决您的问题这一事实强调了问题的“错误”性质。
第二个要点似乎是潜在的错误:我假设某些迭代器没有正确计算虚拟列,因此应该检查 createdBy_user_id
的外键似乎混淆了列,实际上针对用户 table 检查 year
的值(在本例中为“20”)。因此,如果您的用户 table 中有一个 ID 为“20”的用户,外键实际上会接受这个,无论您尝试插入的 createdBy_user_id
的值是什么,请参阅 MySQL 5.7.29 fiddle.
除非您有特定理由使用虚拟列,否则使用 stored
可能是理智的做法。
我有 2 个数据库表“用户”和“任务”。
任务 table 包含 createdBy_user_id
和 updatedBy_user_id
列的两个外键,它们都引用 users.id
。如果我尝试向 tasks
插入一个条目(在确保外键引用的用户存在之后),如下所示:
INSERT INTO tasks (createdBy_user_id,updatedBy_user_id,noOfYear,createdAt,updatedAt,status,customer_id)
VALUES (1,1,1,NOW(),NOW(),"open",1)
查询失败,错误 1452:
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test`.`tasks`, CONSTRAINT `user_id_fk_constr` FOREIGN KEY (`createdBy_user_id`) REFERENCES `users` (`id`))
我不知道为什么会这样,因为如果我删除约束,一切都会正常。 “updatedBy_user_id”列不会发生同样的错误,这让这让人感到困惑。
这些表具有以下 DDL:
用户table:
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`active` tinyint(1) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`email_confirmed_at` datetime DEFAULT NULL,
`username` varchar(50) NOT NULL,
`password` varchar(255) NOT NULL,
`first_name` varchar(50) DEFAULT NULL,
`last_name` varchar(50) DEFAULT NULL,
`job` varchar(64) DEFAULT NULL,
`position` varchar(64) DEFAULT NULL,
`specialKnowledge` text,
`tasks` text,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
任务table:
CREATE TABLE `tasks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`createdAt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updatedAt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`noOfYear` int(11) NOT NULL,
`year` int(11) GENERATED ALWAYS AS (right(year(`createdAt`),2)) VIRTUAL NOT NULL,
`createdBy_user_id` int(11) NOT NULL,
`updatedBy_user_id` int(11) NOT NULL,
`status` enum('open','closed') NOT NULL,
`customer_id` int(11) NOT NULL,
`projectDescription` text,
PRIMARY KEY (`id`),
UNIQUE KEY `tasks_year_unique_constr` (`year`,`noOfYear`),
KEY `user_id_fk_constr` (`createdBy_user_id`),
KEY `customer_id_fk_constr` (`customer_id`),
KEY `user_up_id_fk_constr` (`updatedBy_user_id`),
CONSTRAINT `customer_id_fk_constr` FOREIGN KEY (`customer_id`) REFERENCES `Customer` (`id`),
CONSTRAINT `user_id_fk_constr` FOREIGN KEY (`createdBy_user_id`) REFERENCES `users` (`id`),
CONSTRAINT `user_up_id_fk_constr` FOREIGN KEY (`updatedBy_user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4
如您所见,数据类型匹配并且 table 都使用 InnoDB Engine
。
用户 table 包含一项:
select id,username from users;
+----+----------+
| id | username |
+----+----------+
| 1 | admin |
+----+----------+
1 row in set (0.00 sec)
所以没有明显的原因,为什么插入会失败。你知道我的 table 有什么问题吗?看起来像是软件错误。
这是一个错误。 MySQL 5.7 在生成列和外键方面遇到了一些问题,我认为这是 Bug #79772 Foreign key not allowed when a virtual index exists.
的未修复变体在您的特定情况下,可能取决于您的确切版本,以下任何修改似乎都可以防止该错误的发生:
- 不使用虚拟列,但使其成为
stored
- 不要为直接跟在虚拟列之后的列创建外键,例如将列顺序更改为
year, status, createdBy_user_id, updatedBy_user_id
. - 不要在虚拟列上使用
unique
索引,普通索引应该没问题(至少在修复了链接错误的版本中)。您需要一个唯一的约束,所以这不是一个选项,但是这可以解决您的问题这一事实强调了问题的“错误”性质。
第二个要点似乎是潜在的错误:我假设某些迭代器没有正确计算虚拟列,因此应该检查 createdBy_user_id
的外键似乎混淆了列,实际上针对用户 table 检查 year
的值(在本例中为“20”)。因此,如果您的用户 table 中有一个 ID 为“20”的用户,外键实际上会接受这个,无论您尝试插入的 createdBy_user_id
的值是什么,请参阅 MySQL 5.7.29 fiddle.
除非您有特定理由使用虚拟列,否则使用 stored
可能是理智的做法。