Récupérer le dernier auto-incrément MySQL avec PHP

Quand un script ajoute un enregistrement dans une table MySQL, il est parfois utile d’en mettre d’autres à jour. Et pour cela, on peut avoir besoin de récupérer le dernier identifiant d’auto-increment. La fonction PHP mysql_insert_id() le fera très bien… sous certaines conditions.

Petit rappel sur les connexions MySQL

Avant de faire l’opération, voici un petit rappel du comportement des connexions MySQL ouvertes par PHP.

La plupart du temps, la connexion avec MySQL est établie par la fonction PHP mysql_connect(). Si un deuxième appel est fait avec les mêmes arguments, la première connexion sera à nouveau utilisée. Le paramètre new_link modifie ce comportement et permet à mysql_connect() d’ouvrir à chaque fois une nouvelle connexion, même si mysql_connect() a été appelée avec les mêmes paramètres.

Lorsque le script utilise une connexion persistante (par la fonction PHP mysql_pconnect()), le comportement est le même, à deux différences (importantes !) près :

  • Lors de la connexion, la fonction essaie de trouver une connexion persistante déjà ouverte, avec le même nom d’utilisateur et le même mot de passe. Dans ce cas, son identifiant est retourné sans ouvrir de nouvelle connexion.
  • La connexion au serveur MySQL n’est pas coupée à la fin du script. Le lien est conservé pour un prochain accès, même si on utilise la fonction mysql_close().

Au passage, notez que les connexions persitantes ne fonctionnent qu’avec PHP en version module.

Et l’auto-increment dans tout ça ?

L’auto-increment sera retourné par la fonction PHP mysql_insert_id(), mais il y a des risques de ne pas récupérer celui que vous attendez !

mysql_insert_id() retourne le dernier identifiant généré par un champ de type AUTO_INCREMENT, sur la connexion MySQL courante ou sur la connexion spécifiée par le paramètre link_identifier. Mais les surprises sont nombreuses :

  • Si votre colonne AUTO_INCREMENT est une colonne de type BIGINT, la valeur retournée par mysql_insert_id() sera incorrecte. À la place, il faut utiliser la fonction MySQL LAST_INSERT_ID() directement dans une requête SQL.
  • mysql_insert_id() ne fonctionne pas avec un REPLACE.
  • Si vous utilisez dans votre requête SQL un INSERT IGNORE en précisant un identifiant existant dans la table, l’enregistrement ne sera pas créé, ce qui est normal puisqu’il ne peut y avoir deux identifiants égaux. Par contre, mysql_insert_id() vous retournera l’auto-increment suivant, comme si l’enregistrement avait été ajouté !
  • Si vous faites des insertions multiples (par exemple, INSERT INTO table1 (champ1, champs2) SELECT champ1, champs2 FROM table2 WHERE champ1=2), mysql_insert_id() retournera l’identifiant du premier enregistrement ajouté !

J’arrête là le massacre car il y a bien d’autres cas. Pour s’en sortir, il faut privilégier la fonction MySQL LAST_INSERT_ID() (placée directement dans une requête SQL) et verrouiller la table :

mysql_query("LOCK TABLES table_exemple WRITE");
mysql_query("SET AUTOCOMMIT = 0");
mysql_query("INSERT INTO table_exemple (champ1, champ2) VALUES ('toto1','toto2')");
$mysql_id = mysql_query("SELECT LAST_INSERT_ID()");
mysql_query("COMMIT");
mysql_query("UNLOCK TABLES");
Avertissement !
Cet article est très ancien et son contenu peut s'avérer obsolète.

Commentaires

OoI

Sinon, y a plus simple avec cette requête, car si il n’existe plus aucune valeur dans la table, LAST_INSERT_ID() renvoi NULL:

SELECT AUTO_INCREMENT as last_id FROM INFORMATION_SCHEMA.TABLES WHERE table_name = 'nom_de_la_table'
30 novembre 2006, 3h08 · Répondre

Christophe

> Ool
Oui, cette requête est un autre moyen d’obtenir le dernier auto-increment. On pourrait donc avoir le code :

mysql_query("INSERT INTO table_exemple (champ1, champ2) VALUES  ('toto1','toto2')");
$mysql_id = mysql_query("SELECT AUTO_INCREMENT as last_id FROM INFORMATION_SCHEMA.TABLES WHERE table_name = 'table_exemple'");

à la place de :

mysql_query("INSERT INTO table_exemple (champ1, champ2) VALUES  ('toto1','toto2')");
$mysql_id = mysql_query("SELECT LAST_INSERT_ID()");

Mais cela ne règle pas le problème de fond, à savoir : obtient-on réellement la valeur attendue ? Sans verrouiller la table, je ne vois pas ce que cela apporte de plus. Mais je peux me tromper…

30 novembre 2006, 15h12 · Répondre

dfg

Merci pour ces explications très utiles ! (même plus d’un an après le post !)

25 mai 2007, 9h45 · Répondre

bgdc

Tjrs aussi utile 2 ans apres le poste 🙂

5 juillet 2008, 16h39 · Répondre

Michael

Excellent,
Je viens d’apprendre une nouvelle méthode.
Merci de cette explication.

12 septembre 2008, 14h31 · Répondre

Loch-Nar

« Notez aussi que la valeur de retour de la fonction SQL LAST_INSERT_ID() contient toujours la valeur d’AUTO_INCREMENT la plus à jour. Cette valeur n’est pas remise à zéro lors de l’exécution d’autre requêtes car elle est maintenue pour le serveur.  »

Last_insert_id ne prend pas en compte la connexion courante et n’est jamais nul contrairement à mysql_insert_id.

16 octobre 2008, 13h59 · Répondre

Loïc

Bonjour,

Il y a quelque chose qui m’intrigue dans la portion de code SQL.

Le LOCK TABLES, je comprends, ça permet d’interdire à d’autres threads de faire des modifications dans la table et donc éviter que l’auto-incrément soit changé.

Mais quel est l’intérêt de désactiver l’AUTO-COMMIT ?

À ce que j’ai compris, désactiver l’AUTO-COMMIT permet de se créer un « environnement local » et les modifications qui sont enregistrées dans cet « environnement local » sont réellement écrites sur le serveur lorsqu’on appelle l’instruction COMMIT.
Le truc, c’est que lorsque la table est « lockée », l’ « environnement local » et le serveur sont confondus, vu que personne d’autre ne peut lire ou écrire …

Si vous pouvez m’éclairer sur ce point …
Merci

26 janvier 2009, 1h28 · Répondre

Christophe

Il y a une raison toute simple : un commit déverrouille la table. S’il est automatique, la table sera libérée et un autre enregistrement peut être effectué par un autre script avant de faire le SELECT LAST_INSERT_ID(). Donc la valeur peut être fausse…

C’est une saine habitude de mettre ensemble le verrouillage et le commit manuel.

La question finale est : « dans ce cas, à quoi sert le UNLOCK TABLES à la fin ? ». Eventuellement à déverrouiller les tables qui n’ont pas été affectées par un commit.

28 janvier 2009, 0h27 · Répondre

Lony

Merci je viens de comprendre cette notion même 15ans après le poste

14 septembre 2024, 0h41 · Répondre

Christophe Le Bot

Avec plaisir ! Content de voir que ça sert toujours 😉

14 septembre 2024, 12h06 ·

caro63

Bonjour, j’essaie de trouver une solution pour récupérer le numéro id le plus grand de cette façon:
printf( » ID le plus grand %d\n », mysql_insert_id());
Mais sa me retourne le dernier id enregistré et non le pas le plus grand.
Il faudrait que j’utilise LAST_INSERT_ID mais je ne sai pas comment l’écrire.
Pouvez-vous m’aider?
Merci d’avance.

29 janvier 2009, 0h56 · Répondre

Christophe

En reprenant le code en exemple dans l’article, il suffit de placer votre instruction à la fin :

mysql_query("LOCK TABLES table_exemple WRITE");  
mysql_query("SET AUTOCOMMIT = 0");  
mysql_query("INSERT INTO table_exemple (champ1, champ2) VALUES  ('toto1','toto2')");  
$mysql_id = mysql_query("SELECT LAST_INSERT_ID()");  
mysql_query("COMMIT");  
mysql_query("UNLOCK TABLES");
printf(” ID le plus grand %d\n”, $mysql_id);
4 février 2009, 20h09 · Répondre

Stef

Bonjour,
Ca ne répond pas à la question de Caro63 (ni la mienne):
Le code affiche le dernier identifiant, mais pas la dernière valeur insérée générée par l’autoincrement…
Comment faire ?

14 avril 2009, 10h22 · Répondre

raf

Bonjour !
En rapport avec ce billet, je cherchais à récupérer la valeur du prochain auto-incrément d’une table … comme ce billet a été mon point de départ dans mes recherches et que j’ai cherché un petit bout de temps avant d’entrevoir la solution, je complète ici avec la solution finalement trouvée (ça peut dépanner) :

$latablecible = "une_table_pour_exemple";
$sql = "SHOW TABLE STATUS LIKE '".$latablecible."'"; 
$result = mysql_query ($sql);
$ligne = mysql_fetch_assoc($result);
$valeurFuturIncrement = $ligne['Auto_increment'];
3 novembre 2009, 12h17 · Répondre

Christophe

@raf

C’est une excellente astuce, ça ! Cela semble étonnant d’avoir besoin de cette info, mais c’est très courant. J’ai eu le cas avec Magento aujourd’hui : il fallait envoyer le n° de commande à un web service entre sa validation et son écriture en base.

Merci pour cette contribution.

6 novembre 2009, 22h52 · Répondre

herve

Bonjour,
tres interressant tes infos Merci beaucoup
Je suis confronté a un bug lors de l’utilisation de mysql_insert_id()
Si quelqu’un a une idée merci

$sql11="INSERT INTO cahier_texte  ( classe ,  type) VALUES ('$nom_classe','00')";
					
					mysql_query("LOCK TABLE cahier_texte");
					// on recupere le dernier id auto increment de la table   cahier_texte
					mysql_query($sql11);
					$recup_id_cahiertexte=mysql_insert_id() ;
					mysql_query("UNLOCK TABLES");
//on met le dernier id de la table cahier de texte dansla table donnees
$sql="INSERT INTO donnees (id_cahier_texte,commentaire) VALUES ('$recup_id_cahiertexte','$commentaire')";			
					mysql_query($sql);

Parfois (rarement 0.01%) la table donnee n’a pas le bon ID

Si quelqu’un a une idée Merci

12 août 2010, 9h54 · Répondre

franky

Je pense que c’est ton commit qui n’est pas bon… relis le code de la fin de l’article…
A+ utile plus de 5 ans après…

13 septembre 2011, 14h01 · Répondre

chanteur06

salut , une question : comment je remplace ça svp?

	$id_resto_tmp = mysql_insert_id();
		mysql_query('INSERT INTO users_restos SET id_user=\''.$_SESSION['config_user']['id_user'].'\', id_resto=\''.$id_resto_tmp.'\',date=NOW()');

Merci

20 décembre 2011, 20h53 · Répondre

Jejeleponey

Bonjour,

La méthode de Ool m’a été plus utile que le LAST_INSERT_ID dont je n’arrivais pas à palier certains problèmes évoqué dans le post original.

Qui aurais pu croire qu’un post soit utile presque 6 ans après 🙂

28 novembre 2012, 14h41 · Répondre

Serge tsimba

bonjour j’ai le même problème de récupérer le dernier enregistrement auto incrément inseré, le résultat généré est « Resource id #5 » or moi j’ai plus de 10 enregistrements.

 mysql_query("LOCK TABLES raison_social WRITE");  
                mysql_query("SET AUTOCOMMIT = 0");
                $requette="INSERT INTO raison_social(nom,postnom,prenom,civilite,nature_carte,numero_carte,telephone,e_mail,pseudo,code_cat_an) VALUES('$nom','$postnom','$prenom','$etatCivil','$natureCarte','$numeroCarte','$telephone','$eMail','$pseudo','$categorieAnonceur')";
                mysql_query($requette) or die ("echec de l'execution de la requete." .mysql_error());;
               $recup_Id=  mysql_query("SELECT LAST_INSERT_ID()");
               mysql_query("COMMIT");  
               mysql_query("UNLOCK TABLES"); 
               echo "$recup_Id";
3 septembre 2014, 7h53 · Répondre

Nils

On compte toujours les années ?
Ne surtout pas supprimer ce fil très intéressant.
Merci à l’auteur et à tous les contributeurs pour cette discussion très instructive.

6 novembre 2014, 10h39 · Répondre

Thy

Sujet toujours *très* utile en 2015 !

Je réponds à Serge tsimba qui récupère un « Resource id ».
(Et à tous ceux qui ont le même souci mais qui ne le disent pas) 🙂

mysql_query ne renvoie pas directement le dernier numéro, il faut la traiter comme n’importe quel select :

$resInsert = mysql_query("SELECT LAST_INSERT_ID()");
if($rowInsert=mysql_fetch_array($resInsert))
{
    $dernierNumInsere=$rowInsert[0];
    echo "Dernier insert : $dernierNumInsere";
}
else
    echo "Oops on a un souci.";
12 novembre 2015, 12h35 · Répondre

Ajouter un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *