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.
1 De Palleas -
N'empêche il vaut quand même mieux utiliser mysql_insert_id() non ? Dans l'absolu c'est quand même moins risqué...
2 De Nicolas -
> N'empêche il vaut quand même mieux utiliser mysql_insert_id() non ? Dans l'absolu c'est quand même moins risqué...
Tu n'aurais pas lu un peu en diagonal ? C'est justement ce que j'essaie d'expliquer!
3 De Palleas -
J'avoue que je m'embrouille un peu dans le code...
Si je resume, dans le premier script tu fait une insertion tout ce qu'il y a de plus basique et tu affiches juste après l'id inseré. Dans le script n°2 tu affiches le maximum puis tu fais une requete d'insertion puis tu réaffiches l'id maximum. Jusque la j'ai l'impression d'avoir compris...
Par contre la ou je sombre dans la catatonie (le regard vide devant le code, l'electroencephalogramme de la grenouille :s) c'est quand tu veux appeler d'abord le script 2 pouis ensuite le premier script... Je ne vois pas bien ou est le probleme puisque dans la linéarité d'interpretation du code (la vache c'est beau ce que je dis ;) ) le script 2 recupera bien l'id inseré et le script n°1, faisant une nouvelle insertion, va recuperer un autre dernier id...
Nan j'crois vraiment que j'ai pas compris la fin :$
4 De Nicolas -
As-tu tout simplement testé ? Je serais tenté de dire que non en voyant tes remarques. Me trompe-je ? Ce que j'ai tenté de faire est de simuler une requête qui se ferait entre l'insertion et la récupération de l'indenfiant nouvellement créé dans le script n°2 (basé sur le max) pour le mettre en défaut et prouver ainsi que ce n'était pas la bonne méthode. Fais les tests et tu comprendras.
p.s: Oublie la première requête; c'est juste pour avoir l'identifiant maximum avant l'insertion. On peut s'en passer.