Ist die LAST_INSERT_ID () - Funktion von MySql garantiert korrekt?

36

Wenn ich eine einzelne Zeile INSERTzu einer Tabelle mit einer AUTO_INCREMENTSpalte mache, möchte ich die LAST_INSERT_ID()Funktion verwenden, um den neuen AUTO_INCREMENTWert zurückzugeben, der für diese Zeile gespeichert ist.

Da viele Microsoft SQL Server-Entwickler und -Administratoren zweifellos wissen, dass die entsprechende Funktionalität in SQL Server ( SCOPE_IDENTITYund @@IDENTITY) nicht ohne Probleme war .

Ich kenne den MySQL-Dokumentstatus:

Die generierte ID wird pro Verbindung auf dem Server verwaltet . Dies bedeutet, dass der von der Funktion an einen bestimmten Client zurückgegebene AUTO_INCREMENTWert der erste Wert ist, der für die letzte Anweisung generiert wurde, die sich auf eine AUTO_INCREMENTSpalte dieses Clients auswirkt . Dieser Wert kann von anderen Clients nicht beeinflusst werden, selbst wenn sie AUTO_INCREMENTeigene Werte generieren . Durch dieses Verhalten wird sichergestellt, dass jeder Client seine eigene ID abrufen kann, ohne die Aktivität anderer Clients zu berücksichtigen und ohne dass Sperren oder Transaktionen erforderlich sind.

(Quelle)

und sogar so weit gehen zu sagen:

Die gleichzeitige Verwendung von LAST_INSERT_ID()und AUTO_INCREMENTSpalten von mehreren Clients ist vollkommen gültig.

(Quelle)

Gibt es bekannte Risiken oder Szenarien, die dazu führen können, dass LAST_INSERT_ID()nicht der richtige Wert zurückgegeben wird?

Ich verwende MySQL 5.5 unter CentOS 5.5 x64 und Fedora 16 x64 und der InnoDB-Engine.

Kev
quelle

Antworten:

35

Ein paar Vorsichtsmaßnahmen, auf die ich hinweisen möchte, wenn ich Folgendes verwende LAST_INSERT_ID:

  1. Ich weiß, dass Sie einzeilige Einfügungen erwähnt haben. Bei mehrzeiligen Einfügungen LAST_INSERT_ID()wird jedoch der Wert der ersten eingefügten Zeile (nicht der letzten) zurückgegeben.

  2. Wenn die Einfügung fehlschlug, LAST_INSERT_ID()wäre undefiniert. Gleiches gilt für automatische Rollbacks von Transaktionen (aufgrund von Fehlern).

  3. Wenn Sie eine erfolgreiche Transaktion einfügen und dennoch eine ausgeben ROLLBACK, wird LAST_INSERT_ID()diese vor dem Rollback beibehalten.

  4. Bei der Verwendung und anweisungsbasierten Replikation gibt es einige Einschränkungen . Das erste ist, wenn es in einem Trigger oder einer Funktion verwendet wird. Das zweite ist das seltenere Szenario, in dem die Spalte auto_increment Teil eines zusammengesetzten Primärschlüssels ist und nicht die erste Spalte des Schlüssels.AUTO_INCREMENTLAST_INSERT_ID

Derek Downey
quelle
7

Um Punkt 2 in der Antwort von DTest weiter auszudehnen:

Bei den von mir verwendeten MySQL-Versionen empfiehlt es sich, den Wert von LAST_INSERT_ID vor jedem Codeblock, in dem Sie eine Einfügung durchführen möchten , explizit zurückzusetzen.

Das kann so gemacht werden:

-- initialize the LAST_INSERT_ID to some flag value:
SELECT LAST_INSERT_ID( some_flag_init_value_of_your_choice );
-- perform the insert  
INSERT INTO ttt (ccc) VALUES (vvv);
-- retrieve the id of the inserted row:  
SELECT LAST_INSERT_ID();

Nachdem die obige Anweisungsreihe ausgeführt wurde, können Sie überprüfen, ob die Einfügung Auswirkungen hatte, indem Sie überprüfen, ob LAST_INSERT_ID am Ende der Ausführung noch auf "some_flag_init_value_of_your_choice" gesetzt war.

Andernfalls kann es zu folgenden Problemen kommen:

INSERT INTO ttt ( ccc ) VALUES ( 'a' );    -- assume this succeeds.
SELECT LAST_INSERT_ID();                   -- this will return the unique id of the new row with value 'a'.
INSERT INTO ttt ( ccc ) VALUES ( 'b' );    -- assume this FAILS.
SELECT LAST_INSERT_ID();                   -- this will STILL RETURN the unique id of the row with 'a'.

Da die zweite Einfügung fehlgeschlagen ist , haben Sie möglicherweise erwartet, dass der zweite Aufruf von LAST_INSERT_ID entweder NULL zurückgibt oder eine leere Ergebnismenge (null Zeilen) erzeugt. Die Tatsache, dass immer noch eine gültige Ganzzahl zurückgegeben wird, kann Sie in die Irre führen, dass die zweite Einfügung SUCCEEDED war, wenn dies nicht der Fall war.

Die Dinge werden AUCH WEIRDER, wenn Sie bedenken, dass LAST_INSERT_ID weiterhin die letzte erfolgreiche eindeutige ID beibehält und wiederholt, selbst wenn nachfolgende fehlgeschlagene Einfügeanweisungen andere Tabellen als die Tabelle ansprechen , die die letzte erfolgreiche eindeutige ID erzeugt hat. Mit anderen Worten, Sie fügen in die Tabelle TA ein und erhalten eine ID von 5, dann fügen Sie in TB ein (was jedoch fehlschlägt), aber Sie sehen immer noch eine 5. Basierend darauf glauben Sie , dass Sie gerade eine neue Zeile in TA erstellt haben mit der ID 5 und einer neuen Zeile in TB mit der ID 5, während es in der Realität entweder keine Zeile in TB mit der ID 5 gibt, oder es gibt eine solche Zeile, aber sie hat eigentlich nichts mit Code zu tun, den Sie gerade haben lief.

pestophag
quelle
2
Erstens hätten Sie die Existenz von nicht dazu nutzen sollen, um last_insert_id()zu beurteilen, ob eine Abfrage erfolgreich war. Es ist immerhin die letzte eingefügte ID, die Werte enthält, die Sie benötigen, wenn Sie bereits gewusst haben, dass Sie erfolgreich waren.
Pacerier