TROUVER LE PROCHAIN ID DE LIBRE.

cs_depression Messages postés 100 Date d'inscription mardi 7 novembre 2000 Statut Membre Dernière intervention 13 juillet 2009 - 12 sept. 2007 à 12:28
pbaboon Messages postés 1 Date d'inscription lundi 28 mars 2005 Statut Membre Dernière intervention 19 septembre 2007 - 19 sept. 2007 à 23:07
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/44056-trouver-le-prochain-id-de-libre

pbaboon Messages postés 1 Date d'inscription lundi 28 mars 2005 Statut Membre Dernière intervention 19 septembre 2007
19 sept. 2007 à 23:07
Bonjour à tous,

Je rajouterai, au fait que ça n'a aucun intêret de boucher les trous dans les id, que cela ne peut amener que des problèmes surtout s'il y a plusieurs utilisateurs qui saisissent des données en même temps :
le même id (max id+1) peut etre attribué plusieurs fois à des utilisateurs différents, tant qu'il n'aurra pas été enregistré dans la bd, et donc lors de l'enregistrement pour les utilisateurs suivants, des doublons ou des blocages à l'insert peuvent apparaître suivant la nature de la cle concernée (unique ou pas)...
Quand à moi je préfère laisser mysql gérer ça tous seul...c'est son job...
... peu-etre penser aussi à last_insert()
bonne continuation
SuperTonic Messages postés 53 Date d'inscription mercredi 24 juillet 2002 Statut Membre Dernière intervention 16 mars 2011
14 sept. 2007 à 14:08
Ok !! j'ai compris... Je vais lire cette doc.
Je vais également ajouté des LIMIT à mes requêtes....
Evangun Messages postés 1980 Date d'inscription dimanche 20 février 2005 Statut Membre Dernière intervention 24 septembre 2012 4
14 sept. 2007 à 14:02
Merci pour ton article Coucou.
Pour les benchmarks, pourquoi ne pas faire la somme du temps d'exécution de 100 000 instructions ? On y verrait plus clair. Le nombre ROWS de EXPLAIN n'est pas significatif, ça ne veut pas dire que MySQL va parcourir plus de lignes pour un > + LIMIT. C'est uniquement le nombre de résultats possibles, point.

Ta conclusion qui est de dire qu'ordonner une table selon l'ordre où les lignes sont les plus sollicitées me paraît tout à fait logique. Ca revient à faire ORDER une seule fois et pas à chaque SELECT. Sauf que j'en reviens au problème de base :

1) ce n'est pas le métier de la clé primaire de servir pour l'ordre. D'ailleurs une nouvelle ligne vient prendre un id libre au pif, en tout cas dans cette source.
2) sous MyIsam, la clé primaire ne définit PAS l'ordre des lignes dans la table. Une nouvelle ligne ne sera pas accédée plus vite qu'elle porte l'id 3 ou l'id 265972545.

Donc fondamentalement, boucher les trous des ID ne sert à rien et causent des pbs. Ensuite si tu veux réordonner ta table à chaque insertion, libre à toi, ça peut être un choix judicieux dans certains cas précis, mais il n'y a pas de rapport avec le fait de boucher les id libres.

Je signale quand même qu'avec ta technique Coucou, je pense que tu ne peux pas utiliser de clé index, puisqu'au moment de la recherche ça viendrait à l'encontre de la façon dont tu as réordonné ta table par toi-même. Ce qui est coûteux pour toutes les requêtes autres que ta (ton unique) requête optimisée. A moins qu'il soit possible de spécifier quand on fait un SELECT de tenir compte ou pas des colonnes indexées ? Mais je n'en ai jamais entendu parler.

SuperTonic : tout simplement, si tu fais SELECT name where id 5; MySQL va trouver la ligne, puis continuer pour voir s'il n'y en a pas d'autre ou id 5. Ce qui est idiot parce que tu sais que chaque id et unique, donc il nen trouvera rien. Avec LIMIT tu lui dit d'arrêter de chercher.
Tu peux aller lire la doc officielle de MySQL, il y a un chapitre entier sur l'optimisation.
coucou747 Messages postés 12303 Date d'inscription mardi 10 février 2004 Statut Membre Dernière intervention 30 juillet 2012 44
14 sept. 2007 à 12:02
je viens de vous faire un bench...
http://blogs.codes-sources.com/coucou747/archive/2007/09/14/bench-sur-la-pagination-sous-mysql.aspx
mais meme ton WHERE + limit, ca revient a faire :
WHERE champ_dans_l_ordre >= page*nbr_par_page LIMIT nbr_par_page
donc ton champ doit-etre dans l'ordre...
le explain semble indiquer que... les benchs sont toutefois plus reserves...
psykocrash Messages postés 240 Date d'inscription vendredi 14 juin 2002 Statut Membre Dernière intervention 17 mars 2009
14 sept. 2007 à 11:58
Cette fonction est là pour trouver le prochain id, elle ne réorganise pas les enregistrements. Elle n'est donc pas à exécuter après un delete mais avant un insert.

@SuperTonic : Si tu indiques correctement les conditions, oui. C'est important (mais pas indispensable) car il permet de ne pas mobiliser plus de ressources que nécessaire. C'est de l'optimisation. Pourquoi charger en mémoire tous les enregistrements quand tu n'as besoin que des 10 premiers, ou que du 11ème au 20ème (par exemple...) ?
SuperTonic Messages postés 53 Date d'inscription mercredi 24 juillet 2002 Statut Membre Dernière intervention 16 mars 2011
14 sept. 2007 à 08:52
Bonjour à tous.
Je suit avec pertinance vos discutions... d'une simple petite fonction, je déchaine des discutions intéressantes ... (je suis fier de moi lol )
J'aimerai revenir sur le LIMIT.
Je cite EVANGUN : "il faut toujours spécifier un LIMIT, même si on sait qu'on ne va récupérér qu'un seul enregistrement"

Voilà bien un truc que je ne fais jamais. Pouvez m'expliquer en quoi c'est important.
si je fais un limit à 30 et que mon résultat est à la 35eme ligne ? il va le trouver tout de même ou pas ??
Evangun Messages postés 1980 Date d'inscription dimanche 20 février 2005 Statut Membre Dernière intervention 24 septembre 2012 4
14 sept. 2007 à 01:37
... ou pas. Je me vois mal faire un update sur toute une table, ainsi que sur tous les enregistrements des autres tables liés à ces index, pour un supposé avantage qui n'existe pas...
L'idée d'avoir un index "sans trou" est peut-être sympa esthétiquement parlant, mais mauvaise.
coucou747 Messages postés 12303 Date d'inscription mardi 10 février 2004 Statut Membre Dernière intervention 30 juillet 2012 44
14 sept. 2007 à 00:56
c'est a chaque suppression qu'on doit reindexer en fait...
Evangun Messages postés 1980 Date d'inscription dimanche 20 février 2005 Statut Membre Dernière intervention 24 septembre 2012 4
13 sept. 2007 à 23:44
Mais de toute façon le SELECT parcourt toutes les lignes de la base, peu importe BETWEEN ou > !! Donc mieux vaut >. Et par défaut avec MyIsam, il parcourt toutes les lignes dans l'ordre où elles ont été insérées, donc même si on bouche les index libres, ça ne change rien. Et si on spécifie LIMIT, MySQL s'arrêtera aussitôt après avoir trouvé ce nombre de lignes, c'est pour ça qu'il faut toujours spécifier un LIMIT, même si on sait qu'on ne va récupérér qu'un seul enregistrement.

Bref, à moins que l'index serve aussi pour ordonner les lignes de la table (ce qui n'est pas une bonne idée) et qu'on reclasse la table après chaque insertion (grosse perte de perfs), ça ne sert vraiment à rien de combler les index libres ! il faut abandonner cette idée.
coucou747 Messages postés 12303 Date d'inscription mardi 10 février 2004 Statut Membre Dernière intervention 30 juillet 2012 44
13 sept. 2007 à 21:03
une comparaison mais un select sur beaucoup plus de lignes pour n'en prendre que 10 au final... le between est bien meilleur
Evangun Messages postés 1980 Date d'inscription dimanche 20 février 2005 Statut Membre Dernière intervention 24 septembre 2012 4
13 sept. 2007 à 18:16
Pour la pagination Coucou... quel est le rapport avec les clés primaires ?
un id n'est pas là pour servir à mettre un ordre, il est là pour l'unicité. Mauvaise idée à mon avis de t'en servir pour ordonner les lignes. Ce sera

-> SELECT record LIMIT 10, 20; et peu importe s'il y a des trous dans les index ou pas. Donc si tu fais ORDER BY, ce sera dans un cas comme dans l'autre.

Et au passage, dans le cas où on comble les trous et où ne veut pas utiliser LIMIT, autant faire WHERE id > XX LIMIT 10 et pas utiliser BETWEEN : c'est probablement plus rapide car il ne fait qu'une comparaison par ligne au lieu de deux.

Moi non plus je ne pense que combler les vides soit une bonne idée. Ca n'a objectivement aucun intérêt (ou alors Coucou faudra que tu détailles ton histoire de pagination parce que je ne te suis pas du tout) et n'amène que des problèmes quand les tables ne sont pas indépendantes.
psykocrash Messages postés 240 Date d'inscription vendredi 14 juin 2002 Statut Membre Dernière intervention 17 mars 2009
13 sept. 2007 à 10:58
Le temps perdu est pour les INSERT (c'est là qu'on calcule le id, donc aussi coté utilisateurs), pas lors des delete.
Et je ne vois toujours pas pourquoi tu n'utiliserais pas between, que tu peux utiliser même en ayant des id qui ont sauté.
coucou747 Messages postés 12303 Date d'inscription mardi 10 février 2004 Statut Membre Dernière intervention 30 juillet 2012 44
13 sept. 2007 à 06:48
ce temps tu le perds lors des delete... en general dans la partie admin d'un site... a cote de tout les select qui seront acceleres dans la partie utilisateur...
psykocrash Messages postés 240 Date d'inscription vendredi 14 juin 2002 Statut Membre Dernière intervention 17 mars 2009
13 sept. 2007 à 00:42
coucou747, le temps que tu gagnes a utiliser between au lieu de order by ... limit, tu le perds à repasser en revue toute ta table à la recherche d'un id "libéré". L'un dans l'autre, moi je préfère ne pas toucher aux clés primaires pour garder une certaine "logique de progression" dans l'enregistrement des tuples. Et puis je vois pas ce qui t'empêche d'utiliser between dans le cas où certains id ont sauté, il suffit de bien penser ton application et tes requêtes dès le départ (je te fait confiance pour ça) et ça tourne sans problème.
coucou747 Messages postés 12303 Date d'inscription mardi 10 février 2004 Statut Membre Dernière intervention 30 juillet 2012 44
12 sept. 2007 à 21:13
psykocrash, combler un trou dans une table, c'est avoir le plaisir ensuite de faire une pagination sans voir de lenteurs au chargement...
un select where id between ... and ... sera plus rapide qu'un order by ... limit ...
pour ton probleme de clefs etrangeres, il est simple de mettre un nouveau champ pour chaque parametre possible de pagination...
SuperTonic Messages postés 53 Date d'inscription mercredi 24 juillet 2002 Statut Membre Dernière intervention 16 mars 2011
12 sept. 2007 à 20:34
Mes tables sont totalements indépendantes des autres.
Id 4 dans table 1 ne correspond à AUCUN autre enregistrement référencé par cet ID dans les autres tables. Par ailleurs s'il y a un 'trou' c'est que tous les enregistrements des tables connexes ont été supprimés également (quand relation il existe)

Je peux donc combler les trous sans crainte de rencontrer un conflit d'intégrité référentielle.
MAIS
effectivement s'il devait y avoir une relation (n,n) il ne faut pas le faire !
Sinon la clé ne définierai plus les bonnes valeurs....

C'est une précision importante !!
MERCI à toi l'ami.
psykocrash Messages postés 240 Date d'inscription vendredi 14 juin 2002 Statut Membre Dernière intervention 17 mars 2009
12 sept. 2007 à 20:26
Je suis étonné que personne n'ai fait la remarque suivante : il ne faut surtout pas faire ce genre d'opérations quand on utilise des clés étrangères. Ca crée des conflits d'intégrité référentielle. D'ailleurs je vois toujours pas pourquoi les débutants (je parle en général, SuperTonic) cherchent toujours à combler ces "trous" dans les tables ??
SuperTonic Messages postés 53 Date d'inscription mercredi 24 juillet 2002 Statut Membre Dernière intervention 16 mars 2011
12 sept. 2007 à 13:30
Exact !
Je n'y avais pas pensé. ça peut effectivement être interessant.
coockiesch Messages postés 2268 Date d'inscription mercredi 27 novembre 2002 Statut Membre Dernière intervention 13 septembre 2013 4
12 sept. 2007 à 13:23
Ok, désolé, j'avais pas vu! :-)
Par contre, si tu fais une fonction, il peut être intéressant de ne pas faire le or die, mais de renvoyer une valeur spéciale...

@++

R@f
SuperTonic Messages postés 53 Date d'inscription mercredi 24 juillet 2002 Statut Membre Dernière intervention 16 mars 2011
12 sept. 2007 à 13:23
IDEM mais sous la forme de 2 fonctions.
---------------------------------------------------------
function maxid($table, $id_contact)
{
//en fonction de la table les reqêtes différent un peu
switch($table)
{
case 'contacts':
$requete= "SELECT max(id_contact) FROM $table WHERE id_user='{$_SESSION['id_user']}'";
break;
default:
$requete="SELECT max(id) FROM $table WHERE id_contact='$id_contact' AND id_user='{$_SESSION['id_user']}'";
}

//on recherche le prochain id pour cette adresse
@mysql_free_result($result);
//echo $requete;
$result=mysql_query($requete,$_SESSION['linkdb']);
if (!$result) {die('Erreur num: '.mysql_errno($_SESSION['linkdb']).'
Échec de la requête : inF_nextid
('.$requete.'): '.mysql_error()); }
$val= @mysql_result($result, 0);
$max=$val+1;
return $max ;
}

---------------------------------------------------------
---------------------------------------------------------

function nextid($table, $id_contact)
{
// On recherche le MAX (appel Fonct.1)
$MAX= maxid($table, $id_contact);
//echo "Val MAX: $MAX
";

switch($table)
{
case 'contacts':
$requetesub=mysql_query("SELECT id_contact as id FROM $table WHERE id_user='{$_SESSION['id_user']}' ORDER by id_contact ASC") or die('Erreur num: '.mysql_errno($_SESSION['linkdb']).'
Échec de la requête : inF_nextid1
('.$req.'): '.mysql_error());
break;
default:
$requetesub=mysql_query("SELECT id FROM $table WHERE id_contact='$id_contact' AND id_user='{$_SESSION['id_user']}' ORDER by id ASC") or die('Erreur num: '.mysql_errno($_SESSION['linkdb']).'
Échec de la requête : inF_nextid2
('.$req.'): '.mysql_error());
}

$totalsub = mysql_num_rows($requetesub);
// Pour info/test on affiche des résultats
//echo "$totalsub ID relemnt occupé ( ";
//while ($row = mysql_fetch_array($requetesub, MYSQL_ASSOC)) {
// echo " {$row['id']} ";
//}
//echo")<hr>";
//On remet le pointeur au début. afin de parcourir de nouveau le tableau
// uniquement si on exécute le While précédent mis en comment.
//mysql_data_seek($requetesub, 0);

$i=-1; //(-1 pour tester la valeur 0)
$p=0; //sera le pointeur.

while($i<$MAX)
{
$i++;
//echo "
Test de la valeur : {$i}
";
$row = mysql_fetch_array($requetesub, MYSQL_ASSOC);
$val = $row['id'];

if($val==$i)
{
//echo"La valeur est = à i -> la preuve :($val=$i)";
//on bouge le pointeur d'1 point
$p++;
if($p==$MAX)
{
return $MAX;
}else{
mysql_data_seek($requetesub, $p);
}
} else {
//echo"ID libre trouvé : $i ($val =$i)";
//on ne déplace pas le pointeur
mysql_data_seek($requetesub, $p);
return $i;
}
}
}

Bon courage à tous.
SuperTonic Messages postés 53 Date d'inscription mercredi 24 juillet 2002 Statut Membre Dernière intervention 16 mars 2011
12 sept. 2007 à 13:17
Bonjour.
Merci de ta réponse constructive.
Au bout de ma chaine (caché en fait par la form se trouve la chaine " or die(mysql_error()); "
qui reviens à ton " if( !$ret ) "

Pour la fonction, je suis en train de la faire.
Je la post dés qu'elle est terminée. (Je laisserai par contre les ECHO mais en commentaires)
Je sais que ça allourdi l'affichage, mais on est bien content de les trouver quand on essai de comprendre.
A bientot donc.
coockiesch Messages postés 2268 Date d'inscription mercredi 27 novembre 2002 Statut Membre Dernière intervention 13 septembre 2013 4
12 sept. 2007 à 12:56
Salut! :-)

Quelques conseils:

mysql_query est toujours suivi d'un test pour vérifier la bonne exécution de la requête:
$ret = mysql_query( "SELECT ...." );
if( !$ret )
// traitement de l'erreur, on ne va pas plus loin

Ou, idem:
if( !( $ret = mysql_query( "SELECT ..." ) ) )
// traitement de l'erreur, on ne va pas plus loin

Ca évite des erreurs sur mysql_num_rows, mysql_fetch_*, ...

Si tu as le choix, privilégie ' ' à " ".

// ---- ICI on ferai un RETURN de l'ID si on fais une fonction ----
>> Justement, tu pourrais proposer une fonction, non? :-)

@++

R@f
cs_depression Messages postés 100 Date d'inscription mardi 7 novembre 2000 Statut Membre Dernière intervention 13 juillet 2009
12 sept. 2007 à 12:28
Berk, c'est crade.

Au lieu d'utiliser une clé primaire dans ta table, utilise une clé unique, ce sera plus rapide et moins affreux pour les performances.
Rejoignez-nous