2009-02-14 6 views
688

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

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

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

ответ

858

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

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

  • Вставка дубликат ключа в столбцах с PRIMARY KEY или UNIQUE ограничений.
  • Вставка NULL в столбец с ограничением NOT NULL.
  • Вставка строки в секционированную таблицу, но значения, которые вы вставляете, не сопоставляются с разделом.

Если вы используете REPLACE, MySQL фактически делает DELETE, за которым следует INSERT внутренне, которая имеет некоторые неожиданные побочные эффекты:

  • Новый автоинкрементным ID выделяется.
  • Зависимые строки с внешними ключами могут быть удалены (если вы используете каскадные внешние ключи), а также предотвратите 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 делает удалить исходную строку и вставляет новую строку, генерируя и хранения нового автоинкрементируемого ID:

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

insert ignore i believe игнорирует ошибки – IEnumerator

+0

Он превращает ошибки в предупреждения. Я добавил некоторые подробности выше, для случаев ошибок, которые игнорируются при использовании INSERT IGNORE. Могут быть и другие случаи. –

5

Replace Входит как опция. Или вы можете проверить это с помощью

IF NOT EXISTS(QUERY) Then INSERT 

Вставьте или удалите, затем вставьте. Сначала я предпочитаю проверить IF NOT EXISTS.

+0

Спасибо за быстрый ответ. Я предполагаю, что это будет похоже на ON DUPLICATE KEY UPDATE, поскольку оно будет выполнять ненужное обновление. Это кажется расточительным, но я не уверен. Любое из них должно работать. Мне интересно, знает ли кто, что лучше. –

+4

NTuplip - это решение по-прежнему доступно для условий гонки из вставок путем одновременных транзакций. –

+0

'REPLACE' удаляет все строки в таблице с помощью соответствующих кнопок _any_' PRIMARY' или 'UNIQUE', _then_' INSERTs'.Это потенциально намного больше работы, чем IODKU. –

13

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

+3

Я обеспокоен тем, что буду игнорировать ошибки других чем дублирование. Правильно ли это или INSERT IGNORE игнорирует только игнорирование отказа дублирования? Спасибо! –

+2

Превращает любую ошибку в предупреждение. См. Список таких случаев в моем ответе. –

+0

Это позор; Хотелось бы, чтобы это проигнорировало дублирование неудач. –

7

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

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

+1

replace выполняет удаление и вставку, тогда как повторное обновление ключа обновляет существующую строку. некоторые отличия: auto incrementing id, позиция в строке, куча триггеров – ahnbizcad

15

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

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

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

+6

Я не совсем уверен, что вы подразумеваете под «недопустимыми значениями» и скорректированы на что? Не могли бы вы привести пример или дальнейшее объяснение? – Marenz

+4

Это означает, что если вы вставляете неправильный тип данных в поле при использовании «INSERT IGNORE», данные будут изменены в соответствии с типом данных поля и будет вставлено потенциально недопустимое значение, после чего запрос будет продолжен. Только с «INSERT» возникает ошибка в отношении неправильного типа данных, и запрос будет прерван.Это может быть нормально, когда число вставляется в varchar или текстовое поле, но вставка текстовой строки в поле с числовым типом данных приведет к плохим данным. – codewaggle

+2

@Marenz еще один пример: если в таблице есть столбец не нуль, а в запросе «INSERT IGNORE» не указывается значение для этого столбца, строка будет вставлена ​​с нулевым значением в этом столбце, независимо от того, является ли строгий sql_mode включен. – Shannon

36

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

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

Query OK, 99 rows affected (0.04 sec)

Records: 100 Duplicates: 1 Warnings: 0

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

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

+4

Если вы используете PHP, вам нужно будет использовать ['mysqli_affected_rows()'] (http://php.net/manual/es/mysqli.affected-rows.php), чтобы узнать, действительно ли 'INSERT' получилось. –

+0

Как с MySQL 5.5, так и с MariaDB 10 I _do_ получить сообщение об ошибке «Невозможно добавить или обновить дочернюю строку: ограничение внешнего ключа не выполняется» и никаких строк (даже действительных). – Floris

+2

@Floris Эта ошибка возникает из-за ограничения * внешнего ключа * и не из-за * дублирующего ключа *. Я использую MySQL 5.5.28. При использовании 'INSERT IGNORE', дублирующие ключи игнорируются без ошибок или предупреждений. – toxalot

147

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

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

Я запустил оба дубликата ключа и заменил его. Мои таблицы закончились с ~ 120 тыс. Строк, причем около 30% моих строк были дублирующими. На дублированном ключе заработало 102 секунды, а замена выполнялась через 105 секунд. Для моего случая я придерживаюсь двойного ключа. – crunkchitis

+0

Протестировал выше с помощью MariaDB 10 и получил предупреждение при запуске 'INSERT IGNORE INTO users_partners (uid, pid) VALUES (1,1), (1,2), (1,3), (1,4)'. – Floris

+0

Какую версию MySQL вы использовали для всего этого? –

0

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

1

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

0

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

Синтаксис:

вставки в набор table1 столбец1 = а, б = столбец2 на dulplicate обновление Столбец2 = с;

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

Давайте понимать это утверждение в глубокий:

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

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

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

Итак, если вы хотите вставить новую строку, обновите эту строку в конфликте первичного ключа или уникального индекса. Read more on this link

Смежные вопросы