"INSERT IGNORE" против "INSERT... ON DUPLICATE KEY UPDATE"

Выполняя инструкцию INSERT со многими строками, я хочу пропустить повторяющиеся записи, которые в противном случае могли бы привести к сбою. После некоторых исследований мои варианты выглядят как использование:

  • ON DUPLICATE KEY UPDATE, что подразумевает ненужное обновление за небольшую плату или
  • INSERT IGNORE, что подразумевает приглашение для других видов сбоев в непредсказуемом.

Я прав в этих предположениях? Какой лучший способ просто пропустить строки, которые могут вызвать дубликаты, и просто перейти к другим строкам?

Ответ 1

Я бы порекомендовал использовать INSERT...ON DUPLICATE KEY UPDATE.

Если вы используете INSERT IGNORE, то строка фактически не будет вставлена, если она приведет к дублированию ключа. Но утверждение не приведет к ошибке. Вместо этого он генерирует предупреждение. Эти случаи включают в себя:

  • Вставка дубликата ключа в столбцы с ограничениями PRIMARY KEY или UNIQUE.
  • Вставка NULL в столбец с ограничением 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 |
+----+------+

Ответ 2

Если вы хотите увидеть, что все это значит, вот удар по всему:

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

Итак, у вас это есть. Так как все это было сделано на свежем столе с почти отсутствием данных, а не на производстве, времена для выполнения были микроскопическими и неактуальными. Любой, кто имеет данные в реальном мире, будет более чем рад внести свой вклад.

Ответ 3

Что-то важное для добавления: при использовании INSERT IGNORE и у вас есть ключевые нарушения, MySQL НЕ поднимет предупреждение!

Если вы попытаетесь, например, вставить 100 записей за раз, с одной неисправной, вы получите интерактивный режим:

Query OK, 99 rows affected (0.04 sec)

Records: 100 Duplicates: 1 Warnings: 0

Как вы видите: никаких предупреждений! Такое поведение ошибочно описано в официальной документации Mysql.

Если ваш script должен быть проинформирован, если некоторые записи не были добавлены (из-за нарушений ключа), вы должны вызвать mysql_info() и проанализировать его для значения "Duplicates".

Ответ 4

Я обычно использую INSERT IGNORE, и это похоже на то, какое поведение вы ищите. Пока вы знаете, что строки, которые вызовут конфликты индекса, не будут вставлены, и вы планируете свою программу соответственно, это не должно вызывать никаких проблем.

Ответ 5

Я знаю, что это старо, но я добавлю это примечание, если кто-то еще (например, я) прибудет на эту страницу, пытаясь найти информацию о INSERT..IGNORE.

Как упоминалось выше, если вы используете INSERT..IGNORE, ошибки, возникающие при выполнении инструкции INSERT, рассматриваются как предупреждения.

Одна вещь, которая явно не упоминается, заключается в том, что INSERT..IGNORE приведет к тому, что недопустимые значения будут скорректированы до ближайших значений при вставке (в то время как недопустимые значения приведут к отмене запроса, если ключевое слово IGNORE не использовалось).

Ответ 6

Replace Into выглядит как опция. Или вы можете проверить

IF NOT EXISTS(QUERY) Then INSERT

Это вставляет или удаляет, а затем вставляет. Я, как правило, предпочитаю проверять IF NOT EXISTS.

Ответ 7

ON DUPLICATE KEY UPDATE на самом деле не соответствует стандарту. Это стандартно, как REPLACE. См. SQL MERGE.

По сути, обе команды являются альтернативными версиями синтаксиса стандартных команд.

Ответ 8

Потенциальная опасность INSERT IGNORE. Если вы пытаетесь вставить значение VARCHAR дольше, тогда столбец был определен с: - значение будет усечено и вставлено. EVEN IF строгий режим включен.

Ответ 9

Если при использовании insert ignore с оператором SHOW WARNINGS; в конце вашего набора запросов будет отображаться таблица со всеми предупреждениями, включая идентификаторы, которые были дубликатами.

Ответ 10

Если вы хотите вставить в таблицу и в конфликт первичного ключа или уникальный индекс, он обновит конфликтующую строку вместо того, чтобы вставлять эту строку.

Синтаксис:

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".

Поэтому, если вы хотите вставить новую строку, иначе обновите эту строку в конфликте первичного ключа или уникального индекса.
Подробнее об этой ссылке