La fonction LAST_INSERT_ID () de MySql est-elle garantie d'être correcte?


36

Lorsque je crée une seule ligne INSERTdans une table comportant une AUTO_INCREMENTcolonne, j'aimerais utiliser la LAST_INSERT_ID()fonction pour renvoyer la nouvelle AUTO_INCREMENTvaleur stockée pour cette ligne.

Comme de nombreux développeurs et administrateurs de Microsoft SQL Server savent sans doute que les fonctionnalités équivalentes dans SQL Server ( SCOPE_IDENTITYet @@IDENTITY) ne se sont pas déroulées sans problèmes .

Je connais l'état de la documentation MySQL:

L'ID généré est conservé sur le serveur connexion par connexion . Cela signifie que la valeur renvoyée par la fonction à un client donné est la première AUTO_INCREMENTvaleur générée pour la dernière instruction affectant une AUTO_INCREMENTcolonne de ce client . Cette valeur ne peut pas être affectée par d'autres clients, même s'ils génèrent AUTO_INCREMENTleurs propres valeurs. Ce comportement garantit que chaque client peut récupérer son propre ID sans se préoccuper de l'activité des autres clients et sans avoir besoin de verrous ou de transactions.

(la source)

et même aller jusqu'à dire:

L'utilisation simultanée de LAST_INSERT_ID()et de AUTO_INCREMENTcolonnes de plusieurs clients est parfaitement valide.

(la source)

Existe-t-il des risques ou des scénarios connus pouvant entraîner le LAST_INSERT_ID()non-retour de la valeur correcte?

J'utilise MySQL 5.5 sur CentOS 5.5 x64, Fedora 16 x64 et le moteur InnoDB.

Réponses:


35

Quelques mises en garde que je voudrais signaler lors de l’utilisation de LAST_INSERT_ID:

  1. Je sais que vous avez mentionné des inserts à une seule rangée. Mais lorsque vous effectuez des insertions sur plusieurs lignes, LAST_INSERT_ID()la valeur de la première ligne insérée (et non la dernière) sera renvoyée.

  2. Si l'insertion échouait, ce LAST_INSERT_ID()serait indéfini. Il en va de même pour les annulations automatiques de transactions (dues à des erreurs).

  3. Si vous effectuez une insertion dans une transaction qui réussit et que vous émettez toujours un ROLLBACK, elle LAST_INSERT_ID()resterait telle quelle avant la restauration.

  4. Il y a quelques mises en garde lors de l'utilisation AUTO_INCREMENTet LAST_INSERT_IDde la réplication basée sur des instructions. Le premier étant utilisé dans un déclencheur ou une fonction. Le second est le scénario moins courant dans lequel votre colonne auto_increment fait partie d'une clé primaire composite et n'est pas la première colonne de la clé.


si vous avez "ON DUPLICATE KEY UPDATE" qui renvoie également 0 si rien n'est mis à jour, si vous définissez un champ date_field = now (), il retournera toujours correctement
max4ever

7

Pour développer davantage le point 2 de la réponse donnée par DTest:

Sur les versions de MySQL que j'ai utilisées, il est judicieux de réinitialiser explicitement la valeur de LAST_INSERT_ID avant chaque bloc de code où vous prévoyez d'effectuer une insertion.

Cela peut être fait comme suit:

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

Une fois que la série d'instructions ci-dessus est exécutée, vous saurez si l'insertion a eu un effet en vérifiant si LAST_INSERT_ID était toujours défini sur "some_flag_init_value_of_your_choice" à la fin de l'exécution.

Sinon, vous pouvez vous retrouver avec la situation problématique suivante:

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

Étant donné que la deuxième insertion a échoué , vous vous attendiez peut-être à ce que le deuxième appel à LAST_INSERT_ID renvoie NULL ou produise un ensemble de résultats vide (zéro ligne). Le fait qu'il renvoie toujours un identifiant entier valide peut vous induire en erreur en pensant que le second insère SUCCEEDED alors qu'il ne l'a pas été.

Les choses deviennent EVEN WEIRDER si vous considérez que LAST_INSERT_ID continuera à conserver et à répéter le dernier identifiant unique réussi, même si des instructions d'insertion défaillantes ultérieures ciblent des tables différentes de la table ayant généré le dernier identifiant unique réussi. En d'autres termes, vous insérez dans la table TA et obtenez un identifiant de 5, puis vous insérez dans la TB (mais cela échoue), mais vous voyez toujours un 5. Sur cette base, vous pensez que vous venez de créer une nouvelle ligne dans TA avec id de 5 et une nouvelle ligne dans TB avec id de 5, alors qu'en réalité, il n'existe aucune ligne dans TB avec id de 5, ou bien il existe une telle ligne, mais elle n'a en réalité rien à voir avec le code que vous venez a couru.


2
En premier lieu, vous n'auriez pas dû utiliser l'existence de last_insert_id()pour déterminer si une requête a abouti. C'est le dernier identifiant inséré après tout, contenant les valeurs dont vous avez besoin quand vous saviez déjà que vous aviez réussi.
Pacerier
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.