it-swarm.com.de

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

Beim Ausführen einer INSERT -Anweisung mit vielen Zeilen möchte ich doppelte Einträge überspringen, die andernfalls zu Fehlern führen würden. Nach einigen Recherchen scheinen meine Optionen entweder zu sein:

  • ON DUPLICATE KEY UPDATE, was eine unnötige Aktualisierung mit einigen Kosten impliziert, oder
  • INSERT IGNORE, was eine Aufforderung zu anderen Arten von Misserfolgen impliziert, sich unangekündigt einzuschleichen.

Habe ich recht mit diesen Annahmen? Was ist der beste Weg, um einfach die Zeilen zu überspringen, die Duplikate verursachen könnten, und einfach mit den anderen Zeilen fortzufahren?

814
Thomas G Henry

Ich würde die Verwendung von INSERT...ON DUPLICATE KEY UPDATE empfehlen.

Wenn Sie INSERT IGNORE verwenden, wird die Zeile nicht wirklich eingefügt, wenn sich ein doppelter Schlüssel ergibt. Die Anweisung erzeugt jedoch keinen Fehler. Stattdessen wird eine Warnung generiert. Diese Fälle umfassen:

  • Einfügen eines doppelten Schlüssels in Spalten mit den Einschränkungen PRIMARY KEY oder UNIQUE.
  • Einfügen einer NULL in eine Spalte mit der Einschränkung NOT NULL.
  • Einfügen einer Zeile in eine partitionierte Tabelle, aber die von Ihnen eingefügten Werte werden keiner Partition zugeordnet.

Wenn Sie REPLACE verwenden, führt MySQL tatsächlich intern ein DELETE gefolgt von einem INSERT aus, was einige unerwartete Nebenwirkungen hat:

  • Eine neue Auto-Inkrement-ID wird zugewiesen.
  • Abhängige Zeilen mit Fremdschlüsseln werden möglicherweise gelöscht (wenn Sie kaskadierende Fremdschlüssel verwenden) oder verhindern das REPLACE.
  • Trigger, die bei DELETE ausgelöst werden, werden unnötigerweise ausgeführt.
  • Nebenwirkungen werden auch auf Replikations-Slaves übertragen.

Korrektur: Sowohl REPLACE als auch INSERT...ON DUPLICATE KEY UPDATE sind nicht standardmäßige, proprietäre Erfindungen, die für MySQL spezifisch sind. ANSI SQL 2003 definiert eine MERGE -Anweisung, die dieselbe Anforderung (und mehr) erfüllen kann, MySQL unterstützt jedoch die MERGE -Anweisung nicht.


Ein Benutzer hat versucht, diesen Beitrag zu bearbeiten (die Bearbeitung wurde von den Moderatoren abgelehnt). Die Bearbeitung hat versucht, eine Behauptung hinzuzufügen, dass INSERT...ON DUPLICATE KEY UPDATE die Zuweisung einer neuen Auto-Inkrement-ID bewirkt. Die neue ID wird zwar generiert , aber in der geänderten Zeile nicht verwendet.

Siehe unten stehende Demonstration, getestet mit Percona Server 5.5.28. Die Konfigurationsvariable innodb_autoinc_lock_mode=1 (die Standardeinstellung):

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

Das Obige zeigt, dass die IODKU-Anweisung das Duplikat erkennt und die Aktualisierung aufruft, um den Wert von u zu ändern. Beachten Sie, dass AUTO_INCREMENT=3 angibt, dass eine ID generiert, aber nicht in der Zeile verwendet wurde.

Während REPLACE die ursprüngliche Zeile löscht und eine neue Zeile einfügt, generiert und speichert es eine neue Auto-Inkrement-ID:

mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   20 |
+----+------+
mysql> replace into foo (u) values (20);
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  3 |   20 |
+----+------+
965
Bill Karwin

Für den Fall, dass Sie sehen möchten, was dies alles bedeutet, hier ist ein Schlag für Schlag von allem:

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

Der Primärschlüssel basiert auf beiden Spalten dieser Kurzreferenztabelle. Ein Primärschlüssel erfordert eindeutige Werte.

Lass uns anfangen:

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

beachten Sie, dass oben zu viel zusätzliche Arbeit gespart wurde, indem die Spalte auf sich selbst gesetzt wurde. Es ist kein Update erforderlich

REPLACE INTO users_partners (uid,pid) VALUES (1,1)
...2 row(s) affected

und nun einige mehrzeilige tests:

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

in der Konsole wurden keine weiteren Meldungen generiert, und die 4 Werte in den Tabellendaten sind nun vorhanden. Ich habe alles außer (1,1) gelöscht, damit ich vom gleichen Spielfeld aus testen kann

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

Also da hast du es. Da dies alles auf einem frischen Tisch mit fast keinen Daten und nicht in der Produktion durchgeführt wurde, waren die Ausführungszeiten mikroskopisch und irrelevant. Jeder, der über reale Daten verfügt, ist herzlich eingeladen, diese beizutragen.

168
Paulus Maximus

Noch etwas Wichtiges: Wenn Sie INSERT IGNORE verwenden und dabei Schlüsselverletzungen auftreten, gibt MySQL KEINE Warnung aus!

Wenn Sie beispielsweise versuchen, 100 Datensätze gleichzeitig einzufügen, wobei einer fehlerhaft ist, gelangen Sie in den interaktiven Modus:

Query OK, 99 rows affected (0.04 sec)

Records: 100 Duplicates: 1 Warnings: 0

Wie Sie sehen: Keine Warnungen! Dieses Verhalten ist in der offiziellen MySQL-Dokumentation sogar falsch beschrieben.

Wenn Ihr Skript informiert werden muss und einige Datensätze (aufgrund von Schlüsselverletzungen) nicht hinzugefügt wurden, müssen Sie mysql_info () aufrufen und es auf den Wert "Duplicates" analysieren.

39
Jens

Ich benutze routinemäßig INSERT IGNORE, und es klingt genau nach der Art von Verhalten, nach der Sie auch suchen. Solange Sie wissen, dass Zeilen, die zu Indexkonflikten führen würden, nicht eingefügt werden und Sie Ihr Programm entsprechend planen, sollte dies keine Probleme verursachen.

18
David Z

Ich weiß, dass dies alt ist, aber ich werde diesen Hinweis hinzufügen, falls jemand anders (wie ich) auf diese Seite gelangt und versucht, Informationen zu INSERT..IGNORE zu finden.

Wie oben erwähnt, werden bei Verwendung von INSERT..IGNORE Fehler, die beim Ausführen der INSERT-Anweisung auftreten, stattdessen als Warnungen behandelt.

Eine Sache, die nicht explizit erwähnt wird, ist, dass INSERT..IGNORE dazu führt, dass ungültige Werte beim Einfügen an die nächsten Werte angepasst werden (während ungültige Werte dazu führen, dass die Abfrage abgebrochen wird, wenn das Schlüsselwort IGNORE nicht verwendet wird).

17
Chris

Replace Into scheint eine Option zu sein. Oder Sie können mit überprüfen

IF NOT EXISTS(QUERY) Then INSERT

Dies wird eingefügt oder gelöscht und dann eingefügt. Ich neige dazu, zuerst einen IF NOT EXISTS Check durchzuführen.

8
IEnumerator

ON DUPLICATE KEY UPDATE ist im Standard nicht wirklich . Es ist ungefähr so ​​Standard wie REPLACE. Siehe SQL MERGE .

Im Wesentlichen handelt es sich bei beiden Befehlen um alternative Syntaxversionen von Standardbefehlen.

8
Chris KL

Mögliche Gefahr von INSERT IGNORE. Wenn Sie versuchen, einen VARCHAR-Wert länger einzufügen, wurde die Spalte mit definiert. Der Wert wird abgeschnitten und eingefügt, AUCH WENN der Strict-Modus aktiviert ist.

3
LOL

Wenn Sie insert ignore mit einer SHOW WARNINGS; -Anweisung am Ende Ihres Abfragesatzes verwenden, wird eine Tabelle mit allen Warnungen angezeigt, einschließlich der doppelten IDs.

2
Ray Foss

Wenn Sie in die Tabelle und den Konflikt des Primärschlüssels oder des eindeutigen Index einfügen möchten, wird die in Konflikt stehende Zeile aktualisiert, anstatt diese Zeile einzufügen.

Syntax:

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

Hier sieht diese insert-Anweisung möglicherweise anders aus, als Sie es zuvor gesehen haben. Diese insert-Anweisung versucht, eine Zeile in Tabelle1 mit den Werten a und b in die Spalten column1 und column2 einzufügen.

Lassen Sie uns diese Aussage genauer verstehen:

Beispiel: Hier ist Spalte1 als Primärschlüssel in Tabelle1 definiert.

Wenn in Tabelle1 keine Zeile mit dem Wert "a" in Spalte1 vorhanden ist. Diese Anweisung fügt also eine Zeile in die Tabelle 1 ein.

Wenn sich nun in Tabelle 1 eine Zeile mit dem Wert "a" in Spalte 2 befindet. Diese Anweisung aktualisiert also den Wert von Spalte2 der Zeile mit "c", wobei der Wert von Spalte1 "a" ist.

Wenn Sie also eine neue Zeile einfügen möchten, aktualisieren Sie diese Zeile auf den Konflikt des Primärschlüssels oder des eindeutigen Index.
Lesen Sie mehr auf diesem Link

2
Dilraj Singh