«INSERT IGNORE» против «INSERT… ON DUPLICATE KEY UPDATE»

713

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

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

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

Теги:
insert

10 ответов

870
Лучший ответ

Я бы рекомендовал использовать 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 |
+----+------+
  • 0
    Хорошие головы на побочные эффекты! Благодарю. Я не против, что дубликаты строк не будут вставлены. Это на самом деле то, что я хочу. Что еще игнорируется INSERT IGNORE?
  • 0
    вставить игнорировать я считаю, игнорирует ошибки
Показать ещё 12 комментариев
158

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

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

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

  • 0
    Я запустил оба ключа и заменил их на. Мои таблицы закончились ~ 120K строк с примерно 30% моих строк, являющихся дубликатами. На дубликате ключа пробежал 102 секунды, а на замене пробежал 105 секунд. В моем случае я придерживаюсь дубликата ключа.
  • 0
    Протестировал вышеупомянутое с MariaDB 10 и получил предупреждение при запуске INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4) .
Показать ещё 1 комментарий
38

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

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

Query OK, 99 rows affected (0.04 sec)

Records: 100 Duplicates: 1 Warnings: 0

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

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

  • 6
    Если вы используете PHP, вам нужно использовать mysqli_affected_rows() чтобы узнать, действительно ли произошла INSERT .
  • 0
    В обоих MySQL 5.5 и MariaDB 10 я получаю сообщение об ошибке Cannot add or update a child row: a foreign key constraint fails может и ни одной строки (даже годные) не добавляется.
Показать ещё 1 комментарий
17

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

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

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

  • 6
    Я не совсем уверен, что вы подразумеваете под "недопустимыми значениями" и исправлены к чему? Не могли бы вы привести пример или дальнейшее объяснение?
  • 4
    Это означает, что если вы вставите неправильный тип данных в поле при использовании «INSERT IGNORE», данные будут изменены, чтобы соответствовать типу данных поля, и будет вставлено потенциально недопустимое значение, тогда запрос продолжится. Только с INSERT возникнет ошибка о неправильном типе данных, и запрос будет прерван. Это может быть нормально с числом, вставляемым в varchar или текстовое поле, но вставка текстовой строки в поле с числовым типом данных приведет к получению неверных данных.
Показать ещё 1 комментарий
15

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

  • 4
    Я обеспокоен тем, что буду игнорировать ошибки, кроме дублирования. Это правильно или INSERT IGNORE игнорирует только игнорирует ошибку дублирования? Спасибо!
  • 2
    Любая ошибка превращается в предупреждение. Смотрите список таких случаев в моем ответе.
Показать ещё 4 комментария
8

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

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

  • 1
    replace выполняет удаление и вставку, а при обновлении с помощью дублирующего ключа обновляется существующая строка. некоторые различия: автоматическое увеличение идентификатора, положение строки, куча триггеров
6

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

IF NOT EXISTS(QUERY) Then INSERT

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

  • 0
    Спасибо за быстрый ответ. Я предполагаю повсеместно, но я предполагаю, что это будет похоже на ON DUPLICATE KEY UPDATE в том, что оно будет выполнять ненужное обновление. Это кажется расточительным, но я не уверен. Любой из них должен работать. Мне интересно, кто-нибудь знает, что лучше.
  • 6
    NTuplip - это решение все еще открыто для состязаний от вставок параллельными транзакциями.
Показать ещё 1 комментарий
3

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

2

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

Синтаксис:

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

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

1

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню