La funzione LAST_INSERT_ID () di MySql è garantita come corretta?


36

Quando eseguo una singola riga INSERTsu una tabella con una AUTO_INCREMENTcolonna, vorrei utilizzare la LAST_INSERT_ID()funzione per restituire il nuovo AUTO_INCREMENTvalore "ed" memorizzato per quella riga.

Poiché molti sviluppatori e amministratori di Microsoft SQL Server sono senza dubbio consapevoli che le funzionalità equivalenti in SQL Server ( SCOPE_IDENTITYe @@IDENTITY) non sono state prive di problemi .

Conosco lo stato dei documenti MySQL:

L'ID che è stato generato viene gestito nel server in base alla connessione . Ciò significa che il valore restituito dalla funzione a un determinato client è il primo AUTO_INCREMENTvalore generato per l'ultima istruzione che interessa una AUTO_INCREMENTcolonna da quel client . Questo valore non può essere influenzato da altri client, anche se generano AUTO_INCREMENTvalori propri. Questo comportamento garantisce che ogni client possa recuperare il proprio ID senza preoccuparsi dell'attività di altri client e senza la necessità di blocchi o transazioni.

(fonte)

e arriva persino a dire:

L'uso LAST_INSERT_ID()e le AUTO_INCREMENTcolonne contemporaneamente da più client è perfettamente valido.

(fonte)

Esistono rischi o scenari noti che potrebbero causare la LAST_INSERT_ID()mancata restituzione del valore corretto?

Sto usando MySQL 5.5 su CentOS 5.5 x64 e Fedora 16 x64 e il motore InnoDB.

Risposte:


35

Un paio di avvertenze che vorrei sottolineare quando si utilizza LAST_INSERT_ID:

  1. So che hai menzionato inserti a riga singola. Ma quando si eseguono inserimenti su più righe, LAST_INSERT_ID()verrà restituito il valore della prima riga inserita (non l'ultima).

  2. Se l'inserimento non è riuscito, LAST_INSERT_ID()sarebbe indefinito. Lo stesso vale per i rollback automatici delle transazioni (a causa di errori).

  3. Se si esegue un inserimento in una transazione che ha esito positivo e si continua a emettere un ROLLBACK, LAST_INSERT_ID()verrebbe lasciato com'era prima del rollback.

  4. Ci sono un paio di avvertenze quando si usa AUTO_INCREMENTe LAST_INSERT_IDnella replica basata su istruzioni. Il primo è quando usato in un trigger o in una funzione. Il secondo è lo scenario meno comune in cui la colonna auto_increment fa parte di una chiave primaria composita e non è la prima colonna della chiave.


se hai "ON DUPLICATE KEY UPDATE" che restituisce anche 0 se non viene aggiornato nulla, se imposti date_field = now () restituirà sempre correttamente
max4ever

7

Per espandere ulteriormente il punto numero 2 nella risposta data da DTest:

Sulle versioni di MySQL che ho usato, è una buona idea ripristinare esplicitamente il valore di LAST_INSERT_ID prima di ogni blocco di codice in cui si prevede di eseguire un inserimento.

Questo può essere fatto in questo modo:

-- 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();

Dopo l'esecuzione della serie di istruzioni precedente, saprai se l'insert ha avuto qualche effetto controllando se LAST_INSERT_ID era ancora impostato su "some_flag_init_value_of_your_choice" al termine dell'esecuzione.

Altrimenti, puoi concludere con la seguente situazione problematica:

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'.

Poiché il secondo inserimento non è riuscito , è probabile che ci si aspettasse che la seconda chiamata a LAST_INSERT_ID restituisse NULL o che avrebbe prodotto un set di risultati vuoto (zero righe). Il fatto che restituisca ancora un identificatore intero valido può indurre in errore a pensare che il secondo inserto abbia SUCCESSO quando non lo ha fatto.

Le cose diventano ANCHE WEIRDER se si considera che LAST_INSERT_ID continuerà a conservare e ripetere l'ultimo ID univoco riuscito anche se le successive istruzioni di inserimento non riuscite hanno come target tabelle diverse rispetto alla tabella che ha prodotto l'ultimo ID univoco riuscito. In altre parole, si inserisce nella tabella TA e si ottiene un ID di 5, quindi si inserisce in TB (ma non riesce), ma si vede ancora un 5. Basandosi su questo, si pensa di aver appena creato una nuova riga in TA con id di 5 e una nuova riga in TB con id di 5, mentre in realtà non esiste alcuna riga in TB con id 5, oppure esiste una riga del genere ma in realtà non ha nulla a che fare con qualsiasi codice corse.


2
In primo luogo non avresti dovuto usare l'esistenza di last_insert_id()per giudicare se una query avesse avuto successo. Dopotutto è l'ultimo ID inserito, che contiene i valori di cui hai bisogno quando già sapevi di esserci riuscito.
Pacerier
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.