MySQL ON DUPLICATE KEY - letzte Einfügungs-ID?

132

Ich habe folgende Frage:

INSERT INTO table (a) VALUES (0)
  ON DUPLICATE KEY UPDATE a=1

Ich möchte die ID des Inserts oder des Updates. Normalerweise führe ich eine zweite Abfrage aus, um dies zu erhalten, da insert_id () meiner Meinung nach nur die 'eingefügte' ID und nicht die aktualisierte ID zurückgibt.

Gibt es eine Möglichkeit, die ID der Zeile einzufügen / zu aktualisieren und abzurufen, ohne zwei Abfragen auszuführen?

thekevinscott
quelle
3
Warum testen Sie es nicht selbst, anstatt anzunehmen? Das SQL in der obigen Bearbeitung funktioniert und ist durch meine Tests schneller als das Abfangen eines Einfügungsfehlers, die Verwendung von INSERT IGNORE oder die Auswahl, ob zuerst ein Duplikat vorhanden ist.
Michael Fenwick
4
WARNUNG: Die vorgeschlagene Lösung funktioniert, aber der Wert für auto_increment wird weiterhin erhöht, auch wenn keine Einfügung vorhanden ist. Wenn der doppelte Schlüssel häufig vorkommt, möchten Sie möglicherweise alter table tablename AUTO_INCREMENT = 0;die obige Abfrage ausführen , um große Lücken in Ihren ID-Werten zu vermeiden.
Frank Forte

Antworten:

174

Überprüfen Sie diese Seite: https://web.archive.org/web/20150329004325/https://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
Am Ende der Seite Sie erklären, wie Sie LAST_INSERT_ID für Aktualisierungen sinnvoll machen können, indem Sie einen Ausdruck an diese MySQL-Funktion übergeben.

Aus dem MySQL-Dokumentationsbeispiel:

Wenn eine Tabelle eine Spalte AUTO_INCREMENT enthält und INSERT ... UPDATE eine Zeile einfügt, gibt die Funktion LAST_INSERT_ID () den Wert AUTO_INCREMENT zurück. Wenn die Anweisung stattdessen eine Zeile aktualisiert, ist LAST_INSERT_ID () nicht aussagekräftig. Sie können dies jedoch umgehen, indem Sie LAST_INSERT_ID (Ausdruck) verwenden. Angenommen, id ist die Spalte AUTO_INCREMENT. Fügen Sie Zeilen wie folgt ein, damit LAST_INSERT_ID () für Aktualisierungen sinnvoll ist:

INSERT INTO table (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), c=3;
fredrik
quelle
2
Irgendwie habe ich das beim Betrachten dieser Seite verpasst. Der Update-Teil sieht also so aus: UPDATE id = LAST_INSERT_ID (id) Und das funktioniert hervorragend. Vielen Dank!
Thekevinscott
7
Es wird gesagt, dass die PHP-Funktion mysql_insert_id () in beiden Fällen den korrekten Wert zurückgibt: php.net/manual/en/function.mysql-insert-id.php#59718 .
Jayarjo
2
@PetrPeller - Nun, ohne die MySQL-Interna zu betrachten, bedeutet dies wahrscheinlich, dass ein Wert erzeugt wird, aber dieser Wert hängt nicht mit der Abfrage zusammen, die Sie gerade ausgeführt haben. Mit anderen Worten, ein Problem, dessen Debuggen schwierig ist.
Jason
13
Nach 5.1.12 ist dies angeblich nicht mehr notwendig, aber ich habe heute eine Ausnahme gefunden. Wenn Sie ein Autoincrement-Paket und einen eindeutigen Schlüssel für beispielsweise eine E-Mail-Adresse haben und der Auslöser "Bei doppelter Aktualisierung" basierend auf der E-Mail-Adresse ausgelöst wird, beachten Sie, dass die last_insert_id 'NICHT der Autoincrement-Wert der aktualisierten Zeile ist. Es scheint der zuletzt eingefügte Autoincrement-Wert zu sein. Das macht einen großen Unterschied. Die Umgehung ist dieselbe wie hier gezeigt, nämlich die Verwendung von id = LAST_INSERT_ID (id) in der Aktualisierungsabfrage.
sckd
1
Auf 5.5 @ sckd gilt der Kommentar immer noch.
e18r
37

Um genau zu sein, wenn dies die ursprüngliche Abfrage ist:

INSERT INTO table (a) VALUES (0)
 ON DUPLICATE KEY UPDATE a=1

und 'id' ist der Primärschlüssel für die automatische Inkrementierung, als dies die funktionierende Lösung wäre:

INSERT INTO table (a) VALUES (0)
  ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), a=1

Ist alles hier: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html

Wenn eine Tabelle eine Spalte AUTO_INCREMENT enthält und INSERT ... UPDATE eine Zeile einfügt, gibt die Funktion LAST_INSERT_ID () den Wert AUTO_INCREMENT zurück. Wenn die Anweisung stattdessen eine Zeile aktualisiert, ist LAST_INSERT_ID () nicht aussagekräftig. Sie können dies jedoch umgehen, indem Sie LAST_INSERT_ID (Ausdruck) verwenden. Angenommen, id ist die Spalte AUTO_INCREMENT.

Aleksandar Popovic
quelle
7
Ja, sehen Sie die akzeptierte Antwort für das, was Sie gesagt haben. Keine Notwendigkeit, 3 Jahre alte Beiträge wiederzubeleben. Trotzdem danke für deine Mühe.
FancyPants
1
@tombom Der einzige Grund, warum ich diese Antwort gepostet habe, ist, dass die akzeptierte Antwort nicht korrekt ist - es funktioniert nicht, wenn es nichts zu aktualisieren gibt.
Aleksandar Popovic
2

Sie können sich REPLACE ansehen, bei dem es sich im Wesentlichen um ein Löschen / Einfügen handelt, wenn der Datensatz vorhanden ist. Dies würde jedoch das Feld für die automatische Inkrementierung ändern, falls vorhanden, wodurch die Beziehungen zu anderen Daten unterbrochen werden könnten.

Brent Baisley
quelle
1
Ah ja - ich suche nach etwas, das frühere Ausweise nicht los wird
thekevinscott
Dies kann auch deshalb gefährlich sein, weil dadurch auch andere verwandte Daten gelöscht werden können (aufgrund von Einschränkungen).
Serge
1

Ich bin auf ein Problem gestoßen, wenn ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID (id) den Primärschlüssel um 1 erhöht. Die ID der nächsten Eingabe innerhalb der Sitzung wird also um 2 erhöht

Oleksii Zymovets
quelle
0

Es ist erwähnenswert, und dies mag offensichtlich sein (aber ich werde es hier aus Gründen der Klarheit trotzdem sagen), dass REPLACE die vorhandene übereinstimmende Zeile wegbläst, bevor Sie Ihre neuen Daten einfügen. ON DUPLICATE KEY UPDATE aktualisiert nur die von Ihnen angegebenen Spalten und behält die Zeile bei.

Aus dem Handbuch :

REPLACE funktioniert genau wie INSERT, außer dass, wenn eine alte Zeile in der Tabelle denselben Wert wie eine neue Zeile für einen PRIMARY KEY oder einen UNIQUE-Index hat, die alte Zeile gelöscht wird, bevor die neue Zeile eingefügt wird.

Greg K.
quelle
0

Die vorhandenen Lösungen funktionieren, wenn Sie Autoincrement verwenden. Ich habe eine Situation, in der der Benutzer ein Präfix definieren kann und die Sequenz bei 3000 neu starten sollte. Aufgrund dieses unterschiedlichen Präfixes kann ich keine automatische Inkrementierung verwenden, wodurch last_insert_id für Einfügungen leer wird. Ich habe es mit folgendem gelöst:

INSERT INTO seq_table (prefix, id) VALUES ('$user_prefix', LAST_INSERT_ID(3000)) ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id + 1);
SELECT LAST_INSERT_ID();

Wenn das Präfix vorhanden ist, wird es erhöht und last_insert_id ausgefüllt. Wenn das Präfix nicht vorhanden ist, wird das Präfix mit dem Wert 3000 eingefügt und last_insert_id mit 3000 gefüllt.

Aaron
quelle