MySQL INSERT IGNORE INTO&外键

时间:2011-07-27 18:32:55

标签: mysql insert foreign-keys ignore

为什么在MySQL中,INSERT IGNORE INTO不会将外键约束错误更改为警告?

我正在尝试在表中插入一些记录,我希望MySQL省去导致错误的错误,任何错误,并插入其余的。有没有人有任何建议?

SET FOREIGN_KEY_CHECKS = 0;不是我的答案。因为我希望不会插入违反约束的行。

由于

6 个答案:

答案 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")

请原谅我的示例中过于简化的代码和不良做法 - 这只是为了说明可能的解决方法。