web-dev-qa-db-fra.com

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

Quand je fais une seule ligne INSERT vers une table qui a une colonne AUTO_INCREMENT, J'aimerais utiliser la fonction LAST_INSERT_ID() pour retourner le nouveau AUTO_INCREMENT 'ed valeur stockée pour cette ligne.

Comme de nombreux développeurs et administrateurs de Microsoft SQL Server sont sans aucun doute conscients que la fonctionnalité équivalente dans SQL Server (SCOPE_IDENTITY Et @@IDENTITY) N'a pas été sans ses problèmes .

Je connais l'état des documents MySQL:

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

(source)

et même aller jusqu'à dire:

L'utilisation de colonnes LAST_INSERT_ID() et AUTO_INCREMENT Simultanément à partir de plusieurs clients est parfaitement valable.

(source)

Existe-t-il des risques ou des scénarios connus qui peuvent empêcher LAST_INSERT_ID() de renvoyer la valeur correcte?

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

36
Kev

Quelques mises en garde que je voudrais souligner lors de l'utilisation de LAST_INSERT_ID:

  1. Je sais que vous avez parlé d'inserts à une rangée. Mais lors des insertions sur plusieurs lignes, LAST_INSERT_ID() renverra la valeur de la première ligne insérée (pas la dernière).

  2. Si l'insertion échouait, LAST_INSERT_ID() ne serait pas définie. Il en va de même pour les annulations automatiques de transactions (en raison d'erreurs).

  3. Si vous effectuez une insertion dans une transaction qui réussit et que vous émettez toujours une ROLLBACK, LAST_INSERT_ID() sera laissée telle qu'elle était avant la restauration.

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

36
Derek Downey

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

Sur les versions de MySQL que j'ai utilisées, c'est une bonne idée de explicité réinitialiser 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 la série d'instructions ci-dessus 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 jeu de résultats vide (zéro ligne). Le fait qu'il renvoie toujours un identifiant d'entier valide peut vous induire en erreur en pensant que le deuxième insert a réussi quand il ne l'a pas fait.

Les choses deviennent même plus étranges lorsque vous considérez que LAST_INSERT_ID continuera à conserver et à répéter le dernier identifiant unique réussi, même si les instructions d'insertion échouantes suivantes ciblent différentes tables que la table qui a produit 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 TB (mais il échoue), mais vous voyez toujours un 5. Sur cette base, vous - pensez que vous venez de créer une nouvelle ligne dans TA avec l'ID de 5 et une nouvelle ligne dans TB avec l'ID de 5, alors qu'en réalité soit il n'existe aucune ligne dans TB avec l'id 5, soit il existe une telle ligne mais elle n'a en fait rien à voir avec le code que vous venez d'exécuter.

7
pestophagous