"INSERT IGNORE" vs " INSERT ... ON DUPLICATE KEY UPDATE"

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

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

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

10 ответов


я бы рекомендовал использовать 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 вызывает выделение нового идентификатора автоматического приращения. Это правда, что новый id созданный, но он не используется в измененной строки.

посмотреть демонстрацию ниже, проверены с Фирконом сервер 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 указывает, что id был генерируется, но не используется в строке.

, тогда как 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 и у вас есть ключевые нарушения, MySQL не вызывает предупреждения!

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

Query OK, 99 rows affected (0.04 sec)

Records: 100 Duplicates: 1 Warnings: 0

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

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


Я знаю, что это старый, но я добавлю эту заметку, если кто-то еще (как я) прибывает на эту страницу, пытаясь найти информацию о INSERT..ИГНОРИРОВАТЬ.

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

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


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


на дубликат ключа обновление не действительно в стандарте. Это примерно так же стандартно, как REPLACE. См.SQL MERGE.

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


Replace Into кажется вариантом. Или вы можете проверить с

IF NOT EXISTS(QUERY) Then INSERT

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


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


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


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

синтаксис:

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

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

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

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

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

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

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