Ne pas réinventer la roue.

On répète souvent que lorsqu'on fait un script il ne faut pas réinventer la roue et utiliser au maximum les fonctions natives du langage. Cela a de multiples avantages: c'est optimisé, sans bug,... Mais on n'imagine pas toujours lorsqu'on est débutant à quel point cet adage doit être pris au sérieux!

Prenons un exemple concret pour éclairer le débat. Imaginons deux tables sql liées entre elles par un identifiant numérique:

  • table1: id, champ1, champ2
  • table2: cid, champ3

Si on n'utilise mysql comme base de données on va utiliser en général un entier auto-incrémenté comme clé primaire de la première table et on va se servir de cet indentifiant comme clé étrangère dans l'autre table. On fait l'insertion d'un enregistrement dans la première table:

insert into table1 (champ1, champ2) values('valeur1', 'valeur2');

Jusque là tout va bien. Comment récupérer cet identifiant nouvellement créé ? La première solution qui vient à l'esprit est de récupérer le plus grand identifiant de la table1:

select max(id)as max_id from table1;

Le résultat obtenu risque de ne pas être le résultat escompté: il se peut qu'une nouvelle insertion intervienne avant que l'on récupère l'identifiant. Si le site n'a que 3 visiteurs par mois le risque que cela arrive est faible mais non négligeable pour un site à fort traffic. Ce n'est pas la bonne méthode: il faut utiliser la fonction mysql_insert_id(). Cette fonction récupère l'identifiant auto-incrémenté lié à la dernière insertion, pour la connexion courante.

Essayons de prouver cela avec les deux scripts suivants en simulant une insertion parasite:

<?php
// script n°1 basé sur mysql_insert_id():
$mysql_link
=mysql_connect($host$user$password);
mysql_select_db('db'$mysql_link);

$query_max "select max(id) as max_id from table1";
printf('Plus grand id de la table: %d<br>'
       
current(mysql_fetch_assoc(mysql_query($query_max$mysql_link)))
       );

$query "insert into table1 (champ1, champ2) values('value1', 'value2')";
$result mysql_query($query$mysql_link);
printf('id inséré: %d<br>'mysql_insert_id($mysql_link));
?>

<?php
// script n°2 basé sur le max:
$mysql_link
=mysql_connect($host$user$password);
mysql_select_db('db'$mysql_link);

$query_max "select max(id) as max_id from table1";
printf('Plus grand id de la table: %d<br>'
       
current(mysql_fetch_assoc(mysql_query($query_max$mysql_link)))
       );

$query "insert into table1 (champ1, champ2) values('value1', 'value2')";
$result mysql_query($query1$mysql_link);
sleep(10);

printf('id inséré: %d<br>'
       
current(mysql_fetch_assoc(mysql_query($query_max$mysql_link)))
       );
?>

Imaginons que le plus grand identifiant de la table1 soit 10. On exécute d'abord le script n°2 puis tout de suite après le script n°1. Le scritp n°2 fait une pause de 10 secondes après l'insertion pour simuler une requête qui s'exécuterait avant la récupération du maximum. L'identifiant inséré par le script n°2 sera donc 11 et celui inséré par le script n°1 sera 12. Mais le résultat obtenu n'est pas bon: le scritp n°2 renvoie 12 et le script n°1 renvoie 12.

On pourrait faire l'opération inverse pour mettre en défaut la fonction mysql_insert_id() en faisant une pause de 10 seconde avant l'appel de la fonction. On verrait que la fonction renvoie bien le résultat voulu.

Haut de page