"INSERT IGNORE" против "INSERT ... ON DUPLICATE KEY UPDATE" - PullRequest
805 голосов
/ 14 февраля 2009

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

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

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

Ответы [ 10 ]

961 голосов
/ 14 февраля 2009

Я бы порекомендовал использовать 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 |
+----+------+
167 голосов
/ 21 октября 2011

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

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

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

39 голосов
/ 21 апреля 2011

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

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

Query OK, 99 rows affected (0.04 sec)

Records: 100 Duplicates: 1 Warnings: 0

Как видите: предупреждений нет! Это поведение даже неправильно описано в официальной документации Mysql.

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

18 голосов
/ 14 февраля 2009

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

17 голосов
/ 16 сентября 2010

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

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

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

8 голосов
/ 14 февраля 2009

ON DUPLICATE KEY UPDATE не является действительно в стандарте. Это примерно так же стандартно, как REPLACE. См. SQL MERGE .

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

8 голосов
/ 14 февраля 2009

Replace Похоже, вариант. Или вы можете проверить с помощью

IF NOT EXISTS(QUERY) Then INSERT

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

3 голосов
/ 20 октября 2017

Потенциальная опасность INSERT IGNORE. Если вы пытаетесь вставить значение VARCHAR дольше, чем столбец был определен с - значение будет усечено и вставлено, ДАЖЕ ЕСЛИ строгий режим включен.

2 голосов
/ 08 декабря 2017

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

Синтаксис:

insert into table1 set column1 = a, column2 = b on duplicate update column2 = c;

Теперь здесь этот оператор вставки может выглядеть иначе, чем вы видели ранее. Этот оператор вставки пытается вставить строку в table1 со значениями a и b в столбцы column1 и column2 соответственно.

Давайте разберемся с этим утверждением в глубине:

Например: здесь column1 определяется как первичный ключ в таблице 1.

Теперь, если в table1 нет строки, имеющей значение «a» в column1. Таким образом, этот оператор вставит строку в таблицу1.

Теперь, если в таблице 1 есть строка, имеющая значение «a» в столбце 2. Таким образом, этот оператор обновит значение строки column2 на «c», где значение column1 равно «a».

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

2 голосов
/ 13 марта 2017

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...