RÉINDEXATION DE TABLE

PlatypusGeek - 26 sept. 2012 à 11:10
sazaju Messages postés 48 Date d'inscription lundi 4 août 2008 Statut Membre Dernière intervention 3 juin 2013 - 10 oct. 2012 à 17:58
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/54619-reindexation-de-table

sazaju Messages postés 48 Date d'inscription lundi 4 août 2008 Statut Membre Dernière intervention 3 juin 2013
10 oct. 2012 à 17:58
On m'a déjà fait la remarque alors je la ressort :

En même temps, c'est pas un site de solutions pro, mais amateurs. Le but n'est pas d'avoir une application prête à vendre, mais simplement pratiquer pour mieux comprendre et maîtriser, avec le support d'une communauté pour aider en cas de blocage ou donner de nouvelles idées. Le SGBD peut faire tout seul et mieux ? Tant mieux, mais le but de ce projet est de le faire via PHP. Si l'ID est utilisé dans les liens URL, les marque pages deviennent obsolètes ? Ça peut arriver, donc à ne pas utiliser dans ce cas là (mais c'est pas une règle d'utiliser ces ID, ou encore on peut se contrebalancer des marque pages, question de priorités).

Par conséquent, si tu as des idées sur comment améliorer le truc, c'est bienvenue. Mais si c'est pour dire que c'est mieux fait ailleurs, c'est équivalent à dire "ton projet ne sert à rien". Utile à mentionner, dire de savoir, mais aucun intérêt du point de vue du projet.
D'abord, je n'ai jamais parlé de PhpmyAdmin. Ensuite Phpmyadmin n'a aucune fonctionnalité de ce genre. Enfin, l'exemple que j'ai publié montre comment, avec seulement 4 requêtes et sans aucune boucle, il est possible de laisser la quasi intégralité du traitement au SGBD (Un traitement bien plus rapide en somme). Donc je ne vois aucun rapport avec Phpmyadmin...
Et sinon, un traitement de plus d'une seconde et encore, c'est déjà un traitement très lourd et dangereux.
aventurier19 Messages postés 102 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 23 juillet 2013
10 oct. 2012 à 12:20
-> BONO62
ta 1ère remarque : Comme écrit dans la conclusion (sans doute non lue) "Ce script n'est pas fait pour les tables ayant leur id en relation avec d'autres tables. Il empêchera les tables de se communiquer les bonnes informations."
CEPENDANT si tu tiens tout de même à le faire, il suffit de mettre dans ton array toutes les tables utilisant l'id que tu souhaites réindexer.
ta 2ème remarque : elle revient à dire pourquoi utiliser php quand on peut faire avec phpmyadmin ; pour moi tout simplement parce que c'est possible donc pourquoi s'embêter à voyager entre deux interfaces notamment si la réindexation est une fonction d'une interface d'administration

ta 3ème remarque (le lock) est interessante ; je vais en prendre note. Au fait même sur une grande bdd, sauf si le serveur php et mysql mettent du temps à communiquer entre eux, la réindexation se fait en quelques secondes maximum.
Quelques remarques:
- D'abord, le fait de réindexer une table nuit à la logique d'identification des données. Comment fait une application pour retrouver la bonne ligne si l'id à changé ? Un exemple concret: j'ai un forum ou j'accède aux topics par leur id dans l'url (GET). J'ai l'url d'un topic en marque-page. Si je réindexe la table, mon marque-page est caduc... Pire, si j'ai des relations implicites avec d'autres tables (moteur MyISAM), elles le seront également!
- Vous chouinez pour des performances à première vue. Pourquoi vous vous cassez la tête avec des boucles de requêtes et une réindexation coté appli alors que le SGBD peut prendre quasi intégralement en charge la réindexation de manière parfaitement optimisée (c'est juste un peu le but d'un SGBD) ?
Jetez un œil là dessus... http://pastebin.com/kck6QNte
- Enfin, dans votre cas, vous récupérez le nombre de lignes puis réindexez la table. Cette opération étant très lourde, dans le cas d'une grande table, cette opération pourra prendre du temps. Si vous ne verrouillez pas la table, un insert d'un client concurrent ne sera pas pris en compte. Pire, une fois la réindexation terminée, le prochain insert écrasera celle qui aura eu lieu pendant l'opération.
sazaju Messages postés 48 Date d'inscription lundi 4 août 2008 Statut Membre Dernière intervention 3 juin 2013
2 oct. 2012 à 19:15
Comme vanhayato l'a remarqué (et d'autres après lui), en réindexant tu impliques une perte de cohérence si tu utilise les ID dans d'autres tables. Mais ça n'est pas difficile à gérer. Pour éviter ça, tu peux donner une description des liens à tenir à jour. Par exemple, en demandant de réindexer la table T1 sur son champs id, tu peux aussi dire que la table T2 doit être synchronisée sur le champs id2, que la table T3 doit être synchronisée sur le champs id3, ... Pour cela, tu peux filer un tableau du style $dependencies = array("T2" => "id2", "T3" => "id3", ...) et donner ça en argument (à défaut de retrouver les dépendances à partir de la description de la BDD, qui n'est pas toujours complète). Après, ça se limite à updater toutes les tables liées en changeant l'ancien id par le nouveau (une requête par table).

Une autre optimisation serait de ne pas tout réindexer dès qu'il y a un trou (1, 2, _, 4, 5, 6, 7 => 4=3, 5=4, 6=5, 7=6 soit 4 modifs) mais de replacer les derniers insérés dans le trous (1, 2, _, 4, 5, 6, 7 => 7=3 soit 1 modif). Ça réduit considérablement le nombre de mises à jour des tables (mais bien sûr les id n'aparaissent plus dans l'ordre, sauf si on demande de les trier par id, ce qui me semble être un bon compromis).
cs_claude77260 Messages postés 54 Date d'inscription dimanche 20 décembre 2009 Statut Membre Dernière intervention 8 avril 2013
1 oct. 2012 à 10:12
Bonjour

la réindexation ce fait sur une table et ne peut être utilisée pour des tables liées par leurs ID
il ne faut pas cracher dans la soupe, contrairement à beaucoup d'autres, sa function fonctionne, et si on en a pas besoin on passe son chemin

Bonne journée à tous
cs_schtroumf Messages postés 59 Date d'inscription mercredi 8 octobre 2003 Statut Membre Dernière intervention 22 juillet 2008
1 oct. 2012 à 09:53
Bonjour,
La ré-indexation de table ne sert globalement à rien (on a des trous d'index et alors ?) et est dangereuse dans le cas d'une base relationnelle (on perds justement les relations) et est une des opérations les plus lourdes que l'on peut faire sur une table.
En résumé, c'est lourd, dangereux et on ne gagne pas en performances.

Toutefois, si vous voulez absolument le faire, la ré-indexation peut se faire de la manière suivante:
ALTER TABLE `ma_table` DROP COLUMN `mon_id`;
ALTER TABLE `ma_table` ADD `mon_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;
rayman223 Messages postés 24 Date d'inscription dimanche 9 décembre 2007 Statut Membre Dernière intervention 7 avril 2013
1 oct. 2012 à 09:46
Bonjour,
Je n'ai pas le temps de parcourir le script mais une simple question me vient à l'esprit :
Est-ce que ton script tiens compte des liaisons entre tables ? sinon, en ré-indexant séparément les tables, les liaisons seront toutes corrompues.
cs_claude77260 Messages postés 54 Date d'inscription dimanche 20 décembre 2009 Statut Membre Dernière intervention 8 avril 2013
30 sept. 2012 à 09:18
je ne connaissais pas les transactions, mais c'est moins universel , utile pour celui qui connais les caractéristiques de sa base et tables

à priori InnoDB, BDB seulement

bon dimanche
PlatypusGeek
30 sept. 2012 à 00:03
Histoire d'aller plus loin l'idéal serait de placer tout ça dans transaction.
Avant de faire la requète de réindexation tu ouvre la transaction mysql.
Si les 2 requêtes ce sont bien passé tu commit sinon tu rollback.

http://dev.mysql.com/doc/refman/5.0/fr/commit.html
cs_claude77260 Messages postés 54 Date d'inscription dimanche 20 décembre 2009 Statut Membre Dernière intervention 8 avril 2013
29 sept. 2012 à 10:55
ton message de bonne fin est optimiste, il marche meme en erreur

je te propose

$RESULT1=mysql_query("UPDATE `$table` SET `$id` = $i+1 WHERE `$id` = $liste_id");
}
// Réinitialisation de la variable d'auto-incrémentation à sa valeur minimale$RESULT2mysql_query("ALTER TABLE `$table` AUTO_INCREMENT 1");

if (($RESULT1) && ($RESULT2))
{
// Petit message pour signaler que la réindexation de la table de la boucle a été faite
echo '
La réindexation de la table `'.$table.'` s\'est déroulée avec succès !
';
}
else
{
echo '
** ERREUR ** Sur la réindexation de la table `'.$table.'`** ERREUR **
';
}
cs_claude77260 Messages postés 54 Date d'inscription dimanche 20 décembre 2009 Statut Membre Dernière intervention 8 avril 2013
29 sept. 2012 à 09:14
pour ceux ,qui comme moi ne sont pas trop réveillés le samedi matin
$liste_table array("NomMaTable1"> "NomId_Matable1","NomMaTable2" => "NomId_Matable2","NomMaTable3" => "NomId_Matable3"); // etc.............

et çà fonctionne. merci à vous deux
cs_claude77260 Messages postés 54 Date d'inscription dimanche 20 décembre 2009 Statut Membre Dernière intervention 8 avril 2013
29 sept. 2012 à 08:36
Bonjour à vous deux,

si possible, pour que ce soit parfait, donner un exemple

Pour le tableau, associez chaque index à sa table

Bonne journée
aventurier19 Messages postés 102 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 23 juillet 2013
28 sept. 2012 à 17:13
Je vais suivre ton conseil et la laisser tel que je l'avais pensée au départ une table => un id.

Merci vanhayato pour cette leçon pratique dispensée de manière pédagogique
PlatypusGeek
28 sept. 2012 à 17:04
Attention à ne pas trop élargir le comportement de la fonction.
Tu pourrais te retrouver avec des problèmes de cohérence de donnée. Par exemple si tu utilise l'id d'une table dans une autre table sans avoir de contrainte de clé étrangère, tu va perdre la liaison en réindexant.
Il faut peut être concerver cette fonction uniquement pour des petites table simple et indépendante.
Sinon oui, c'est possible. Ton tableau deviendrais $listeTable array('table1'> array('id11','id12), 'table2' => array('id2));
Du coup dans ton foreach tu refait un foreach sur les id

foreach ($liste_table as $table => $tab_id) {
foreach ($tab_id as id) {
aventurier19 Messages postés 102 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 23 juillet 2013
28 sept. 2012 à 16:39
Eh bien, j'en apprends des choses avec une si petite fonction.
Avant de modifier, j'aimerai savoir si on peut améliorer dans le cas ou une table a plusieurs index
PlatypusGeek
28 sept. 2012 à 15:59
Mieux comme ça, mais encore perfectible du coup ^^

Au lieu de passer 2 paramètre, dont un qui est un tableau.
Tu ne pourrais en passer qu'un du genre :
$listeTable array('table1'> 'id1', 'table2 => 'id2',...);

Ton foreach deviendrait :

foreach ($liste_table as $table => $id) {

Du coup plus besoin d'appeler plusieurs fois la fonction si le nom de tes id changent ^^
aventurier19 Messages postés 102 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 23 juillet 2013
28 sept. 2012 à 15:47
Voila la mise à jour du script est faite. Ce sera surement la dernière.

N'hésitez pas à le noter et donner votre avis.
cs_claude77260 Messages postés 54 Date d'inscription dimanche 20 décembre 2009 Statut Membre Dernière intervention 8 avril 2013
27 sept. 2012 à 19:03
Désolé le résultat est bien 1116 enregistrements compressé à 1116

la fonctionne très bien

mauvais tri des id par la console d'admin de mysql

j'ai pu remettre de l'ordre dans mes tables

Merci a bientôt sur un autre script
aventurier19 Messages postés 102 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 23 juillet 2013
27 sept. 2012 à 18:02
impeccable ; c'était mon but en mieux.
merci vanhayato et claude77260

Je mets à jour prochainement.
cs_claude77260 Messages postés 54 Date d'inscription dimanche 20 décembre 2009 Statut Membre Dernière intervention 8 avril 2013
27 sept. 2012 à 16:19
je viens de faire un essai du script

la réindexation est bonne

sur 1116 enregistrements il me compresse à 1110
mais il garde 1116 enregistrements et numéro du prochain 1117 ?????
cs_claude77260 Messages postés 54 Date d'inscription dimanche 20 décembre 2009 Statut Membre Dernière intervention 8 avril 2013
27 sept. 2012 à 16:03
je voulais dire de faire
// connexion à la base de donnée
include('connexion.php');
//connexion à la table
$table='ma_table';
$id='mon_id';

//Appel à la fonction
REindex_MySQL($table,$id);

function REindex_MySQL($table,$id) {

de façon à ne pas réécrire ton id à chaque fois que veux appeler ta fonction
PlatypusGeek
27 sept. 2012 à 15:58
biensur, il ne faut plus de $table dans le fichier de conf ^^
PlatypusGeek
27 sept. 2012 à 15:56
Ouais quelque chose comme ça.

Perso j'utiliserais un foreach :

function Reindex_MySQL($listeTable){
// Insertion du fichier de configuration
include("config.php");
foreach($listetable as $table){
//ton code non modifier
}
}
aventurier19 Messages postés 102 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 23 juillet 2013
27 sept. 2012 à 15:33
dans la page php une variable du type $table = array("table1", "table2", "table3");
ET
dans la fonction, une boucle du type for ($h = 0; $i < sizeof($table); $h ++) {

[...]

$ids = mysql_query("SELECT `id` FROM `$table[$h]`");

[...]

mysql_query("UPDATE `$table[$h]` SET `id` = $i+1 WHERE `id` = $liste_id");

c'est ça ?
PlatypusGeek
27 sept. 2012 à 15:16
Oui du coup, tu peut prévoir la variable $table pour être un tableau. Si c'est le cas tu boucle sur les tables pour les réindexer ^^
aventurier19 Messages postés 102 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 23 juillet 2013
27 sept. 2012 à 15:09
ok, dans mon appli j'utilise la variable $table dans pas mal de page qui utilisait config.php, de ce fait il était plus pratique pour moi de le mettre dans le fichier config.

Je fais la modif
aventurier19 Messages postés 102 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 23 juillet 2013
27 sept. 2012 à 15:05
mettre $table en variable (pardon) Faire une boucle avec $table dans le but de réindéxer toutes les tables en une fois ?
PlatypusGeek
27 sept. 2012 à 15:02
Pour les parametres que l'on te conseil de passer à la fonction.
Ta fonction deviendrait function REindex_MySQL($table) ce qui permet d'appeler la fonction en lui précisant une table et la rendre indépendante du parametre table de ton fichier de config (qui lui ne devrait avoir que les infos de connexion à la BDD)

Pour le coup de l'id, la je vois pas....
aventurier19 Messages postés 102 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 23 juillet 2013
27 sept. 2012 à 14:55
vanayoto,
Je sais reconnaitre mes torts et effectivement en excluant la requete update, ton idée est la plus optimisée

sur une table de 5 lignes
Mon idée Idée de vanhayato
3.00407409668E-5 2.31266021729E-5
0.000231027603149 0.000236034393311
0.000385999679565 0.000378131866455
0.000509023666382 0.000494956970215
0.000653982162476 0.000607967376709

----

claude77260,
1/ mettre l'id dans une variable, je pense que c'est le $i+1 que tu parlais mais mettre le $i+1 en variable ou le for (on ne peut pas mettre un for en variable ?)

2/ mettre $table en variable dans le but de réindéxer toutes les tables en une fois ?
cs_claude77260 Messages postés 54 Date d'inscription dimanche 20 décembre 2009 Statut Membre Dernière intervention 8 avril 2013
27 sept. 2012 à 12:20
re
dis moi , si tu avais mis l'ID dans une variable, on pouvait la passer dans l'appel de fonction sans avoir à se payer les modifs dans le script à chaque fois
on peut aussi passer $table et la fonction devient autonome
à+
cs_claude77260 Messages postés 54 Date d'inscription dimanche 20 décembre 2009 Statut Membre Dernière intervention 8 avril 2013
27 sept. 2012 à 10:25
Bonjour

une petite fonction à conserver dans la boite à outils

Merci de partager
PlatypusGeek
26 sept. 2012 à 12:55
Etrange et pas logique.... puisque dans ce que je propose on ne passe par la fonction mysql_num_rows qu'une seul fois et dans la boucle fort on lit le contenu d'une variable...

Donc à moins qu'executer la fonction mysql_num_rows prenne moins de temps que lire une variable, je ne vois pas pourquoi ton teste est plus long avec ce que je propose.....

il faudrait le faire avec justement beaucoup plus de ligne (genre 100 000 ^^). Il aura suffit que ta bdd réponde un poil moins vite dans un cas pour biaiser le résultat.

Essai de faire le test sans faire l'update pour ne pas avoir cette perturbation ;)
aventurier19 Messages postés 102 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 23 juillet 2013
26 sept. 2012 à 12:49
Tout compte fait, après un test simple d'exécution sur une table de 73 lignes, il s'avèrent que ton idée est moins bonne que ce que j'ai fait

Mon idée :
for($i=0;$i<mysql_num_rows($ids);$i++) {
$liste_id=mysql_result($ids,$i);mysql_query("UPDATE `$table` SET `id` $i+1 WHERE `id` $liste_id");
}
-- Temps moyen d'exécution 0.0045 seconde

Idée de vanhayato
$compte_id = mysql_num_rows($ids)
for($i=0;$i<$compte_id;$i++) {
$liste_id=mysql_result($ids,$i);mysql_query("UPDATE `$table` SET `id` $i+1 WHERE `id` $liste_id");
}
-- Temps moyen d'exécution 0.0062 seconde

Il n'y a pas beaucoup d'écart avec 73 lignes mais avec 1000 ça se verrait plus.
aventurier19 Messages postés 102 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 23 juillet 2013
26 sept. 2012 à 12:23
Cette fonction est à mon sens à utiliser séparément d'une insertion. Pour mon application en cours, j'ai créé un bouton qui donne la possibilité de réindexer la table en cliquant dessus.

Si pour ta suggestion, j'en prends note et corrige de suite.
PlatypusGeek
26 sept. 2012 à 11:10
Première chose : sort le mysql_num_rows($ids) du for, ça évitera qu'il le fasse à chaque passage de la boucle. Stock la valeur dans une variable ce sera mieux ;)

Sinon, je ne vois pas trop l’intérêt de faire ça, que les ids se suivent ou pas ça ne change rien à la base, à l'accé au donné etc etc....
De plus, c'est valable pour une toute petite table à laquel il n'y a quasi jamais d'insertion (pour pas dire jamais). Imagine que la fonction ce lance au moment d'une insertion, tu risque de te retrouver avec un conflit d'id, voir même un nouveau trou dans tes ids ^^
J'oubli le cas d'une table avec plusieurs insertion à la seconde et des millions de ligne. La c'est tout bonnement impossible à mettre en place...
Rejoignez-nous