为什么在MySQL中,INSERT IGNORE INTO
不会将外键约束错误更改为警告?
我正在尝试在表中插入一些记录,我希望MySQL省去导致错误的错误,任何错误,并插入其余的。有没有人有任何建议?
SET FOREIGN_KEY_CHECKS = 0;
不是我的答案。因为我希望不会插入违反约束的行。
由于
答案 0 :(得分:33)
[新答案]
感谢@NeverEndingQueue提出这个问题。似乎MySQL终于解决了这个问题。我不确定这个问题首先修复了哪个版本,但是现在我使用以下版本进行了测试,问题不再存在了:
mysql> SHOW VARIABLES LIKE "%version%";
+-------------------------+------------------------------+
| Variable_name | Value |
+-------------------------+------------------------------+
| innodb_version | 5.7.22 |
| protocol_version | 10 |
| slave_type_conversions | |
| tls_version | TLSv1,TLSv1.1 |
| version | 5.7.22 |
| version_comment | MySQL Community Server (GPL) |
| version_compile_machine | x86_64 |
| version_compile_os | Linux |
+-------------------------+------------------------------+
要明确:
mysql> INSERT IGNORE INTO child -> VALUES -> (NULL, 1) -> , (NULL, 2) -> , (NULL, 3) -> , (NULL, 4) -> , (NULL, 5) -> , (NULL, 6); Query OK, 4 rows affected, 2 warnings (0.03 sec) Records: 6 Duplicates: 2 Warnings: 2
为了更好地理解最后一个查询的含义以及为什么它显示问题已修复,请继续下面的旧答案。
[OLD ANSWER]
我的解决方案是解决问题的方法,实际的解决方案将始终在MySQL本身内解决问题。
以下步骤解决了我的问题:
a。考虑使用以下表格和数据:
mysql>
CREATE TABLE parent (id INT AUTO_INCREMENT NOT NULL
, PRIMARY KEY (id)
) ENGINE=INNODB;
mysql>
CREATE TABLE child (id INT AUTO_INCREMENT
, parent_id INT
, INDEX par_ind (parent_id)
, PRIMARY KEY (id)
, FOREIGN KEY (parent_id) REFERENCES parent(id)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=INNODB;
mysql>
INSERT INTO parent
VALUES (NULL), (NULL), (NULL), (NULL), (NULL), (NULL);
mysql>
SELECT * FROM parent;
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
+----+
b。现在我们需要删除一些行来演示问题:
mysql>
DELETE FROM parent WHERE id IN (3, 5);
<强>℃。问题:当您尝试插入以下子行时出现问题:
mysql>
INSERT IGNORE INTO child
VALUES
(NULL, 1)
, (NULL, 2)
, (NULL, 3)
, (NULL, 4)
, (NULL, 5)
, (NULL, 6);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint f
ails (`test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERE
NCES `parent` (`id`) ON DELETE CASCADE ON UPDATE CASCADE)
mysql>
SELECT * FROM child;
Empty set (0.00 sec)
即使使用IGNORE
关键字,但MySQL取消了请求的操作,因为生成的错误没有变成警告(正如它所预期的那样)。现在问题很明显,让我们看看如何在不面临任何错误的情况下执行最后一次插入语句。
<强> d。解决方案:我将把插入包装到一些其他常量语句中,这些语句既不依赖于插入的记录,也不依赖于它们的编号。
mysql>
SET FOREIGN_KEY_CHECKS = 0;
mysql>
INSERT INTO child
VALUES
(NULL, 1)
, (NULL, 2)
, (NULL, 3)
, (NULL, 4)
, (NULL, 5)
, (NULL, 6);
mysql>
DELETE FROM child WHERE parent_id NOT IN (SELECT id FROM parent);
mysql>
SET FOREIGN_KEY_CHECKS = 1;
我知道这不是最佳的,但只要MySQL没有解决问题,这就是我所知道的最好的。特别是因为如果在PHP中使用mysqli库,所有语句都可以在一个请求中执行。
答案 1 :(得分:6)
我认为问题的最简单的mysql解决方案是加入外键表以验证约束:
INSERT INTO child (parent_id, value2, value3)
SELECT p.id, @new_value2, @new_value3
FROM parent p WHERE p.id = @parent_id
但是你想根据丢失的外键扔掉记录似乎很奇怪。例如,我更喜欢在外键检查时出现INSERT IGNORE错误。
答案 2 :(得分:3)
我认为INSERT IGNORE
旨在忽略来自服务器层的错误,而不是存储引擎层。因此,它将有助于重复键错误(它的主要用例)和某些数据转换,但不会有来自存储引擎层的外键错误。
至于你的具体要求:
我正在尝试将一些记录插入表中,我希望如此 MySQL省略了产生错误,错误和插入的错误 其余的部分。有没有人有任何建议?
为此,我建议使用mysql -f
强制它继续运行,尽管有任何错误。例如,如果您有这样的文件:
insert into child (parent_id, ...) values (bad_parent_id, ...);
insert into child (parent_id, ...) values (good_parent_id, ...);
然后您可以像这样加载该文件,这将插入好行并忽略坏行中的错误:
mysql -f < inserts.sql
答案 3 :(得分:1)
这个问题似乎在MySQL 5.7中得到修复,请参阅https://bugs.mysql.com/bug.php?id=78853。
现在外键约束错误变为警告:
此问题存在于5.1,5.5,5.6版本中,但我看到5.7中已完成一些改进,其中错误已在WL#6614之后转换为警告而非错误。
答案 4 :(得分:0)
INSERT IGNORE只是懒惰的解决方法。您不应该首先插入重复记录。除了主要/唯一与外键不同。并且请记住,IGNORE也会忽略其他错误和警告(除零,数据截断),这通常不是一件好事。
在这种情况下,几乎每次都使用REPLACE而不是INSERT IGNORE更有意义。另一种选择是ON DUPLICATE KEY UPDATE。
在您的情况下,如果您在插入之前无法删除缺少的父项,则可以使用如下临时表来管理相同的事情:
create temporary table tmpTable (col1, col2, ...);
insert into tmpTable values (row1), (row2), ...; -- same insert you already had
alter table tmpTable add key (foreignColumnName); -- only if # of rows is big
insert into table select null /* AI id */, col1, col2 ...
from tmpTable join parentTable on foreignColumnName = parent.id;
此致 何。
答案 5 :(得分:-1)
如果要在数据库中插入行,则可以使用其他查询显式运行此检查。假设你有这些表:
User Pet userId | userName | petId petId | petName -------+----------+------ ------+---------- 1 | Harold | 8 1 | Fido 2 | Fred | 3 3 | Spot 8 | Mittens
并且您想要插入一个新用户(乔治,他有宠物#1)。你可以做点什么
$newUserName = "George"
$petId = "1"
mysql_query("begin")
$result = mysql_query("SELECT petId FROM Pet WHERE petId = $petId LIMIT 1")
if ($result && 1 == mysql_num_rows($result)) {
mysql_query("INSERT INTO User (userName, petId) VALUES ('$newUserName', $petId)")
}
mysql_query("commit")
请原谅我的示例中过于简化的代码和不良做法 - 这只是为了说明可能的解决方法。