2013-12-20 12 views
11

LAST_INSERT_ID() gibt die neueste ID für die aktuelle Verbindung durch eine Auto Increment-Spalte zurückgegeben, aber wie sage ich, ob dieser Wert aus der letzten einfügen ist und nicht von einem früheren Einsatz auf der gleichen Verbindung?Löschen von LAST_INSERT_ID() vor dem Einfügen, um festzustellen, ob die zurückgegebene Nachricht von meinem Einfügen

Angenommen, ich bin mit einer Verbindung aus einem Pool, der eine Reihe eingefügt haben konnte, bevor ich die Verbindung bekam, und ich einen bedingten Einsatz ausführen:

insert into mytable (colA) 
select 'foo' from bar 
where <some condition>; 
select LAST_INSERT_ID(); 

Ich habe keine Möglichkeit zu wissen, ob der Wert zurückgegeben kommt von meiner Einlage. Ich

Eine Möglichkeit gedacht ist:

@previousId := LAST_INSERT_ID(); 
insert into mytable (colA) 
select 'foo' from bar 
where <some condition>; 
select if(LAST_INSERT_ID() != @previousId, LAST_INSERT_ID(), null); 

Gibt es eine Möglichkeit, „klar“ den LAST_INSERT_ID() Wert, so dass ich weiß, dass es ein neuer Wert von meinem SQL verursacht, wenn ein Nicht-Null-Wert zurückgegeben wird?

+0

Dies ist ein teilweise gelöstes Problem. Könnten Sie das ursprüngliche Problem, das Sie lösen möchten, veröffentlichen? –

+1

Was ist falsch an der Verwendung von 'row_count', wie von Digital Chris vorgeschlagen? – rsanchez

Antwort

3

Verwenden ROW_COUNT(), um zu bestimmen, ob Ihr bedingter Einsatz Versuch erfolgreich war, und dann wieder LAST_INSERT_ID() oder einen Standardwert auf der Grundlage dieser:

select IF(ROW_COUNT() > 0, LAST_INSERT_ID(), 0); 
3

Seit LAST_INSERT_ID() ist full von gotchas, verwende ich einen AFTER INSERT Trigger, um die ID in einer Protokolltabelle und in einer Weise, die mit dem Problem aufnehmen zu lösen Ich versuche.

Da in Ihrem Szenario die Einfügung bedingt ist, verwenden Sie eine eindeutige Kennung, um die Einfügung "zu markieren", und prüfen Sie anschließend, ob diese Variable im Einfügeprotokoll vorhanden ist. Wenn das Identifizierungs-Tag vorhanden ist, ist Ihr Einschub aufgetreten und Sie haben die eingefügte ID. Wenn das identifizierende Tag nicht vorhanden ist, ist kein Einfügen aufgetreten.

Referenzimplementierung

DELIMITER $$ 
CREATE TRIGGER MyTable_AI AFTER INSERT ON MyTable FOR EACH ROW 
BEGIN 
    INSERT INTO MyTable_InsertLog (myTableId, ident) VALUES (NEW.myTableId, COALESCE(@tag, CONNECTION_ID())); 
END $$ 
DELIMITER ; 

CREATE TABLE MyTable_InsertLog (
    myTableId BIGINT UNSIGNED PRIMARY KEY NOT NULL REFERENCES MyTable (myTableId), 
    tag CHAR(32) NOT NULL 
); 

Beispiel Verwendung

SET @tag=MD5('A'); 
INSERT INTO MyTable SELECT NULL,colA FROM Foo WHERE colA='whatever'; 
SELECT myTableId FROM MyTable_InsertLog WHERE [email protected]; 
DELETE FROM MyTable_InsertLog WHERE [email protected]; 

Wenn der Einsatz erfolgreich ist, werden Sie Zeilen aus der select - und diese Zeilen werden Ihre ID. Keine Zeilen, keine Einfügung. Löschen Sie in jedem Fall die Ergebnisse aus dem Einfügeprotokoll, damit Sie dieses Tag in nachfolgenden Aufrufen wiederverwenden können.

+0

nach dem Einfügen Trigger würde nicht helfen. Mein App-Code benötigt die ID der gerade hinzugefügten Zeile, aber nur, wenn sie * hinzugefügt wurde. Ich muss in der Lage sein zu unterscheiden, dass die letzte Einfüge-ID, die von der Funktion zurückgegeben wird, von der gerade ausgeführten SQL ist – Bohemian

+0

@Bohemian: Sicher können Sie, wenn ich Ihren Anwendungsfall richtig verstehe. Siehe meine Bearbeitung für ein funktionierendes Beispiel. – bishop

6

Sie spekulieren, dass Sie die vorherige LAST_INSERT_ID() erhalten könnten, aber praktisch sehe ich nicht, wo dieser Fall passieren würde. Wenn Sie eine bedingte Einfügung machen, müssten Sie überprüfen, ob es erfolgreich war, bevor Sie die nächsten Schritte machen. Zum Beispiel würden Sie immer ROW_COUNT() verwenden, um eingefügte/aktualisierte Datensätze zu überprüfen. Pseudo-Code:

insert into mytable (colA) 
select 'foo' from bar 
where <some condition>; 

IF ROW_COUNT() > 0 
    select LAST_INSERT_ID(); 
    -- ...use selected last_insert_id for other updates/inserts... 
END IF; 
2

Die LAST_INSERT_ID() Funktion hat einen ziemlich engen Anwendungsbereich: wie MySQL nicht SQL SEQUENCE s unterstützen, das verwendet wird, um eine Transaktion zu erstellen, dass INSERT s Daten konsistent in mehrere Tabellen, wenn man Referenzen die Ersatzschlüssel aus der anderen Tabelle:

CREATE TABLE foo (id INTEGER PRIMARY KEY AUTO_INCREMENT, value INTEGER NOT NULL); 
CREATE TABLE bar (id INTEGER PRIMARY KEY AUTO_INCREMENT, fooref INTEGER NOT NULL); 
INSERT INTO foo(value) VALUES ('1'); 
INSERT INTO bar(fooref) VALUES (LAST_INSERT_ID()); 

Wenn Sie wirklich, diese verwenden in einem bedingten einfügen wollen, müssen Sie die zweiten INSERT bedingten sowie machen durch das Verbinden der eingefügten Werte gegen die SELECT vom ersten INSERT und anschließend die zusätzlichen Spalten wegwerfen (so dass die zweite INSERT auch Null oder eine Zeile hat).

Ich würde jedoch davon abraten.Die bedingte Einfügung auf diese Weise ist bereits etwas brüchig, und das Aufsetzen darauf wird Ihrer Anwendung keine Stabilität verleihen.

+0

Nicht gut. Es gibt keinen zweiten Einsatz. Die letzte Einfüge-ID wird außerhalb der Datenbank vom App-Code verwendet. Außerdem beantwortet das auch die Frage nicht. – Bohemian

+0

Die Antwort lautet "Es wird nicht erwartet, dass Sie' LAST_INSERT_ID() 'auf diese Weise verwenden." Deshalb habe ich mich nach einer Beschreibung des ursprünglichen Problems gefragt. –

+0

Ich möchte die letzte eingefügte ID aus der SQL in meine Anwendung bekommen, aber meine SQL macht eine bedingte Einfügung, also muss ich in der Lage sein zwischen der letzten eingefügten ID zu unterscheiden, die von meiner Einfügung oder der vorherigen Einfügung für die Verbindung I 'kommt. m verwenden. Wenn es nicht von meinem Insert (Versuch) stammt, möchte ich, dass es null oder null ist. – Bohemian

9

Ich stimme mit @Digital Chris's answer überein, dass Sie nicht feststellen sollten, ob eine Einfügung erfolgreich war oder fehlgeschlagen ist, indem Sie den von LAST_INSERT_ID() zurückgegebenen Wert überprüfen: es gibt mehr direkte Routen wie die betroffene Zeilenanzahl. Dennoch kann es immer noch erforderlich sein, einen "sauberen" Wert von LAST_INSERT_ID() zu erhalten.

Natürlich ein Problem mit dem vorgeschlagenen Lösung (zum Vergleich gegenüber dem vor dem Einsetzen Wert) ist, dass es passieren könnte, dass das Einfügen erfolgreich war und, die er zugewiesenen Auto-erhöhte Wert zufällig ist das gleiche wie die von die vorherige Einfügung (vermutlich auf einer anderen Tabelle). Der Vergleich könnte daher zu der Annahme führen, dass die Insertion fehlgeschlagen ist, obwohl sie tatsächlich gelungen ist.

Ich empfehle, dass, wenn überhaupt möglich, vermeiden Sie die Verwendung der LAST_INSERT_ID() SQL-Funktion für den API-Aufruf mysql_insert_id() (über Ihren Treiber). Wie in der Dokumentation für das letztere erklärt:

mysql_insert_id() kehrt 0, wenn die vorherige Anweisung einen AUTO_INCREMENT Wert nicht verwendet. Wenn Sie den Wert für später speichern möchten, rufen Sie sofort nach der Anweisung, die den Wert generiert, mysql_insert_id() auf.

[ deletia ]

Der Grund für die Unterschiede zwischen LAST_INSERT_ID() und mysql_insert_id() ist, dass LAST_INSERT_ID() leicht gemacht wird, in Skripten zu verwenden, während mysql_insert_id() versucht über genauere Informationen zu liefern, was passiert mit der AUTO_INCREMENT Spalte.

In jedem Fall, wie unter LAST_INSERT_ID(expr) dokumentiert:

Wenn expr wird als Argument an LAST_INSERT_ID() gegeben, wird der Wert des Arguments von der Funktion zurückgegeben und wird als die in Erinnerung nächster Wert, der von LAST_INSERT_ID() zurückgegeben werden soll.

daher vor der Durchführung Ihrer INSERT, könnten Sie zurückgesetzt mit:

SELECT LAST_INSERT_ID(NULL); 

Dies sollte auch den Wert von mysql_insert_id() zurückzusetzen, obwohl die Dokumentation der Aufruf an LAST_INSERT_ID(expr) schlägt vor, innerhalb von ein INSERT nehmen müssen oder UPDATE Anweisung — erfordert möglicherweise Tests zur Überprüfung.Auf jeden Fall sollte es ziemlich trivial sein, um eine solche Aussage no-op, wenn dies erforderlich ist:

INSERT INTO my_table (my_column) SELECT NULL WHERE LAST_INSERT_ID(NULL); 

Es kann sich lohnen, unter Hinweis darauf, dass man auch die identity und last_insert_id Systemvariablen einstellen können (aber nur diese beeinflussen der Wert von LAST_INSERT_ID() und nicht von mysql_insert_id() zurückgegeben):

SET @@last_insert_id := NULL; 
Verwandte Themen