【问题标题】:MySQL (Percona) Error 1452: Cannot add or update a child row for no reasonMySQL (Percona) 错误 1452:无法无故添加或更新子行
【发布时间】:2020-09-01 20:02:44
【问题描述】:

我有 2 个数据库表“用户”和“任务”。 任务表包含两个外键 createdBy_user_idupdatedBy_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:

用户表:

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

任务表:

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 

如您所见,数据类型匹配,两个表都使用InnoDB Engine。 users 表包含一个条目:

select id,username from users;
+----+----------+
| id | username |
+----+----------+
|  1 | admin    |
+----+----------+
1 row in set (0.00 sec)

所以没有明显的原因,为什么插入会失败。你知道我的桌子有什么问题吗?看起来像一个软件错误。

【问题讨论】:

  • 似乎mysql 5.7 mysql 8 的shoprtt comming 没有问题
  • @nbk shoprtt 是什么?
  • mysql 5.7的缺点试试看dbfiddle.uk/…
  • 试试dbfiddle
  • @wchiquito 你知道为什么列的顺序会有所不同吗?

标签: mysql sql foreign-keys percona


【解决方案1】:

这是一个错误。 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 的外键似乎混淆了列并实际检查了year(在本例中为“20”)针对用户表。因此,如果您的 users 表中有一个 id 为“20”的用户,则外键实际上会接受这一点,无论您尝试插入的 createdBy_user_id 的值是什么,请参阅 MySQL 5.7.29 fiddle

除非您有使用虚拟列的特定原因,否则使用stored 可能是明智的做法。

【讨论】:

  • 此列只是一个帮助确保年份(短格式)和(noOfYear)的组合是唯一的。我没有使用stored,因为这会在表中创建冗余数据,从而使用比预期更多的内存。
猜你喜欢
  • 2012-02-06
  • 2018-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-28
  • 1970-01-01
  • 2022-11-25
  • 1970-01-01
相关资源
最近更新 更多