Выполняя инструкцию INSERT
со многими строками, я хочу пропустить повторяющиеся записи, которые в противном случае могли бы привести к сбою. После некоторых исследований мои варианты выглядят как использование:
ON DUPLICATE KEY UPDATE
, что подразумевает ненужное обновление за небольшую плату илиINSERT IGNORE
, что подразумевает приглашение для других видов сбоев в непредсказуемом.Я прав в этих предположениях? Какой лучший способ просто пропустить строки, которые могут вызвать дубликаты, и просто перейти к другим строкам?
Я бы рекомендовал использовать INSERT...ON DUPLICATE KEY UPDATE
.
Если вы используете INSERT IGNORE
, тогда строка фактически не будет вставлена, если она приведет к дублированию ключа. Но оператор не будет генерировать ошибку. Вместо этого он генерирует предупреждение. Эти случаи включают:
PRIMARY KEY
или UNIQUE
.NOT NULL
.Если вы используете REPLACE
, MySQL фактически выполняет DELETE
, за которым следует INSERT
внутри, что имеет некоторые неожиданные побочные эффекты:
REPLACE
.DELETE
, выполняются без необходимости. : как REPLACE
, так и INSERT...ON DUPLICATE KEY UPDATE
являются нестандартными, запатентованными изобретениями, специфичными для MySQL. ANSI SQL 2003 определяет оператор MERGE
, который может решить одну и ту же потребность (и многое другое), но MySQL не поддерживает оператор MERGE
.
Пользователь попытался отредактировать этот пост (изменение было отклонено модераторами). Редактирование попыталось добавить утверждение, что INSERT...ON DUPLICATE KEY UPDATE
вызывает выделение нового идентификатора автоматического инкремента. Это правда, что новый идентификатор сгенерирован, но он не используется в измененной строке.
См. демонстрацию ниже, протестированную с Percona Server 5.5.28. Конфигурационная переменная innodb_autoinc_lock_mode=1
(по умолчанию):
mysql> create table foo (id serial primary key, u int, unique key (u));
mysql> insert into foo (u) values (10);
mysql> select * from foo;
+----+------+
| id | u |
+----+------+
| 1 | 10 |
+----+------+
mysql> show create table foo\G
CREATE TABLE `foo` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`u` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `u` (`u`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
mysql> insert into foo (u) values (10) on duplicate key update u = 20;
mysql> select * from foo;
+----+------+
| id | u |
+----+------+
| 1 | 20 |
+----+------+
mysql> show create table foo\G
CREATE TABLE `foo` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`u` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `u` (`u`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1
Вышеописанное показывает, что оператор IODKU обнаруживает дубликат и вызывает обновление для изменения значения u
. Обратите внимание, что AUTO_INCREMENT=3
указывает, что идентификатор был сгенерирован, но не использовался в строке.
В то время как REPLACE
удаляет исходную строку и вставляет новую строку, генерируя и сохраняя новый идентификатор автоматического инкремента:
mysql> select * from foo;
+----+------+
| id | u |
+----+------+
| 1 | 20 |
+----+------+
mysql> replace into foo (u) values (20);
mysql> select * from foo;
+----+------+
| id | u |
+----+------+
| 3 | 20 |
+----+------+
Если вы хотите увидеть, что все это значит, вот удар по всему:
CREATE TABLE `users_partners` (
`uid` int(11) NOT NULL DEFAULT '0',
`pid` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`uid`,`pid`),
KEY `partner_user` (`pid`,`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
Первичный ключ основан на обоих столбцах этой справочной таблицы. Основной ключ требует уникальных значений.
Пусть начнется:
INSERT INTO users_partners (uid,pid) VALUES (1,1);
...1 row(s) affected
INSERT INTO users_partners (uid,pid) VALUES (1,1);
...Error Code : 1062
...Duplicate entry '1-1' for key 'PRIMARY'
INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1);
...0 row(s) affected
INSERT INTO users_partners (uid,pid) VALUES (1,1) ON DUPLICATE KEY UPDATE uid=uid
...0 row(s) affected
обратите внимание, что выше сохраненная слишком большая работа, установив столбец, равный самому себе, фактическое обновление не требуется
REPLACE INTO users_partners (uid,pid) VALUES (1,1)
...2 row(s) affected
и теперь несколько тестов с несколькими строками:
INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...Error Code : 1062
...Duplicate entry '1-1' for key 'PRIMARY'
INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...3 row(s) affected
в консоли не было создано никаких других сообщений, и теперь в этих таблицах теперь есть эти 4 значения. Я удалил все, кроме (1,1), чтобы я мог тестировать одно и то же игровое поле
INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4) ON DUPLICATE KEY UPDATE uid=uid
...3 row(s) affected
REPLACE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...5 row(s) affected
Итак, у вас это есть. Так как все это было сделано на свежем столе с почти отсутствием данных, а не на производстве, времена для выполнения были микроскопическими и неактуальными. Любой, кто имеет данные в реальном мире, будет более чем рад внести свой вклад.
INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
.
Что-то важное для добавления: при использовании INSERT IGNORE и у вас есть ключевые нарушения, MySQL НЕ поднимет предупреждение!
Если вы попытаетесь, например, вставить 100 записей за раз, с одной неисправной, вы получите интерактивный режим:
Query OK, 99 rows affected (0.04 sec)
Records: 100 Duplicates: 1 Warnings: 0
Как вы видите: никаких предупреждений! Такое поведение ошибочно описано в официальной документации Mysql.
Если ваш script должен быть проинформирован, если некоторые записи не были добавлены (из-за нарушений ключа), вы должны вызвать mysql_info() и проанализировать его для значения "Duplicates".
mysqli_affected_rows()
чтобы узнать, действительно ли произошла INSERT
.
Cannot add or update a child row: a foreign key constraint fails
может и ни одной строки (даже годные) не добавляется.
Я знаю, что это старо, но я добавлю это примечание, если кто-то еще (например, я) прибудет на эту страницу, пытаясь найти информацию о INSERT..IGNORE.
Как упоминалось выше, если вы используете INSERT..IGNORE, ошибки, возникающие при выполнении инструкции INSERT, рассматриваются как предупреждения.
Одна вещь, которая явно не упоминается, заключается в том, что INSERT..IGNORE приведет к тому, что недопустимые значения будут скорректированы до ближайших значений при вставке (в то время как недопустимые значения приведут к отмене запроса, если ключевое слово IGNORE не использовалось).
Я обычно использую INSERT IGNORE
, и это похоже на то, какое поведение вы ищите. Пока вы знаете, что строки, которые вызовут конфликты индекса, не будут вставлены, и вы планируете свою программу соответственно, это не должно вызывать никаких проблем.
ON DUPLICATE KEY UPDATE на самом деле не соответствует стандарту. Это стандартно, как REPLACE. См. SQL MERGE.
По сути, обе команды являются альтернативными версиями синтаксиса стандартных команд.
Replace
Into выглядит как опция. Или вы можете проверить
IF NOT EXISTS(QUERY) Then INSERT
Это вставляет или удаляет, а затем вставляет. Я, как правило, предпочитаю проверять IF NOT EXISTS
.
Потенциальная опасность INSERT IGNORE. Если вы пытаетесь вставить значение VARCHAR дольше, тогда столбец был определен с: - значение будет усечено и вставлено. EVEN IF строгий режим включен.
Если вы хотите вставить в таблицу и в конфликт первичного ключа или уникальный индекс, он обновит конфликтующую строку вместо того, чтобы вставлять эту строку.
Синтаксис:
insert into table1 set column1 = a, column2 = b on duplicate update column2 = c;
Теперь здесь этот оператор вставки может выглядеть по-другому, что вы видели ранее. Этот оператор insert пытается вставить строку в таблицу1 со значениями a и b в столбец столбца1 и столбец2 соответственно.
Дайте понять это выражение в глубине:
Например: здесь column1 определяется как первичный ключ в таблице1.
Теперь, если в таблице 1 нет строки, имеющей значение "a" в столбце 1. Таким образом, этот оператор вставляет строку в таблицу1.
Теперь, если в таблице 1 есть строка, имеющая значение "a" в столбце2. Таким образом, этот оператор обновит значение столбца строк с помощью "c", где значение столбца "a".
Поэтому, если вы хотите вставить новую строку, иначе обновите эту строку в конфликте первичного ключа или уникального индекса.
Подробнее об этой ссылке
Если при использовании insert ignore
с оператором SHOW WARNINGS;
в конце вашего набора запросов будет отображаться таблица со всеми предупреждениями, включая идентификаторы, которые были дубликатами.