LEFT JOIN avec forte cardinalité => tps infini

Répondre


Cette question est un moyen d’empêcher des soumissions automatisées de formulaires par des robots.
Smileys
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: =D> #-o =P~ :^o :non: :priere: 8-|
Voir plus de smileys
  Revue du sujet
 

  Étendre la vue Revue du sujet : LEFT JOIN avec forte cardinalité => tps infini

par cerber » 28 avr. 2005, 10:15

Voila le nouvel explain pour ma super requête

Code : Tout sélectionner

1, 'SIMPLE', 's', 'ALL', '', '', , '', 31073, 'Using filesort' 1, 'SIMPLE', 'v', 'ref', 'PRIMARY,Index_dossier', 'Index_dossier', 4, 'f13mdb.s.Dossier', 32, '' 1, 'SIMPLE', 't', 'ref', 'Tarifs23Fev05GHM', 'Tarifs23Fev05GHM', 256, 'f13mdb.s.GHM', 1, '' 1, 'SIMPLE', 'c', 'ref', 'Code', 'Code', 7, 'f13mdb.v.Code acte cetelic', 1, ''
Tient, pendant que j'y pense, ca vous intéresse de savoir faire des tableaux croisés de type MS Access avec mysql ? (ou vous savez déjà ?)
(En passant je suis preneur pour une solution pour PostgreSQL)

par cerber » 27 avr. 2005, 17:59

gnarf, moi je mange ma casquette en punition pour ma nullité!
Je suis plus au taf là mais je t'enverrais le EXPLAIN final demain

Moi aussi ca m'étonnais qu'accès soit plus rapide que mysql, mais j'avais fini par croire à un bug de mysql (étrange malgré tout) style boucle infinie, tant j'avais du mal à ouvrir le gestionnaire des taches ...

par Ripat » 27 avr. 2005, 17:18

Si access avait été plus rapide que MySQL, j'en aurais mangé ma souris, mon clavier et, ne soyons pas frugal, mon serveur. :wink:

Et, que donne ton EXPLAIN cette fois? (juste pour mon info)

par cerber » 27 avr. 2005, 17:07

Bon, le mot de la fin pour les fans :
l'ordre des clés dans un index à de l'importance, à la manière des group by.
(c'est stupide mais je m'en souvenais plus / j'y ai pas pensé)

voila ce que me donne le explain sur ma requête de stat pour le nombre de rangs comparés dans la table de facturation :
  • PRIMARY KEY (`Numero`,`Dossier`) => 918134 comparaisons
  • PRIMARY KEY (`Dossier`,`Numero`) => 9181 comparaisons
  • PRIMARY KEY (`Dossier`,`Numero`) + INDEX(`Dossier`) => 32 comparaisons
temps d'exécution de la requête avec les BONS index et les left join (pour mémoire avec les mauvais index et le inner c'était 59sec) :
32sec

FIN

par cerber » 27 avr. 2005, 16:50

OK c'est bon, merci a tout ceux qui m'ont aidés
Merci particulier à ripat pour la fonction PROCEDURE analyse() qui est super efficasse pour optimiser la structure (mieux vaut l'exécuter sans jointures c'est plus représentatif)

Le fin mot de l'histoire ? ok je vous le donne :
ripat m'en a donné l'idée dans son dernier message : un probleme d'index.
La base a une structure un peu *étrange*, notament au niveau de la table de facturation. Mon boss (qui sais bien faire des requête dans access, mais qui ne connais pas grand chose à SQL) s'est fait refourguer une table avec le primary index mal placé.
table :

Code : Tout sélectionner

n°ligne, Dossier , .... 01,A,... 02,A,... 03,B,... 04,A,...
le numéro de ligne est un auto increment => il a mis le primary index dessus sans tenir compte du numéro de dossier, mais il a quand même mis un index simple sur les dossier, pour accelerer ses requêtes.
Mais dans la (ma) *logique* sql le primary index aurais du être n°ligne+Dossier => quand j'ai optimisé la base j'ai viré les index superflus (en fait il en avais mis un pour chacune des 10 put*ins de colonnes de la table :axe: => j'y suis allé à la hache) et g laissé le primary uniquement

J'avais tenté de mettre un index sur dossier mais ca avais rien changé => j'avais abandonné ....
MAIS, je m'était planté de table, j'avais mis l'index dans la table client au lieu de le mettre dans la table facture #-o :non:

=> parfois je suis une buse !

Moralité, a trop regarder le probleme on fini par ne plus voir l'évidence

en tout cas, re MERCI à tous

par Ripat » 27 avr. 2005, 15:55

Code : Tout sélectionner

id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE c index PRIMARY 4 46618 Using index 1 SIMPLE f index PRIMARY 8 686119 Using where; Using index; Not exists
Hum, il utilise ton index mais il ne s'en sert pas (vois le nombre de lignes qu'il doit parcourir dans la deuxième table! Il les lit toutes!)

Essaye de supprimer tes PRIMARY KEYS et d'en faire deux index séparés (pas primary)

Code : Tout sélectionner

ALTER TABLE ´fac´ ADD INDEX id (id) ALTER TABLE ´fac´ ADD INDEX dossier (dossier)
Et refais un EXPLAIN pour voir?

Tu devrais avoir seulement quelques centaines de lignes lues dans ta table ´fac´

S'il l'optimiseur MySQL ne trouve pas tes index, charges les en férocité avec USE INDEX ou FORCE INDEX. Ca DOIT marcher que diable. Et mieux que dans Access :wink:

par Invité » 27 avr. 2005, 14:49

Merci pour l'idée ripat mais j'y avais pensé : check + repair + optimize tout est OK
(j'ai eut un doute comme je repartait de données importées depuis accès mais ca a l'air bon)


Un pote me prete temporairement son serveur : http://jrdasm.no-ip.com/temp/cerber.rar (~2Mo) ce sont les fichiers d'une base myisam crée avec mysql 4.1.11 (l'actuelle 4.x)
C'est une base de test crée avec des valeurs random (les vraies données sont très sensibles)
la table client comporte environ 46 000 enreg avec des numéros de dossier entre 0 et 100 000
la table fact contient environ 600 000 enreg avec des numéros de dossier <950 (c'est le résultat d'un premier peuplement que je n'ai pas eut le courage de refaire)
Donc j'ai énormément de clients sans lignes de facturation (~95%) alors que dans la vraie base c'est que 2100 clients sur 31 000

a tester avec la requête :

Code : Tout sélectionner

SELECT * FROM `cli2` c left outer join `fac2` f on `f`.`dossier`=`c`.`dossier` WHERE f.`dossier` is null
le résultat du explain :

Code : Tout sélectionner

"id","select_type","table","type","possible_keys","key","key_len","ref","rows","Extra" 1,"SIMPLE","c","index","","PRIMARY",4,"",46618,"Using index" 1,"SIMPLE","f","index","","PRIMARY",8,"",686119,"Using where; Using index; Not exists"
le procedure analyse() (que je ne connaissais pas) ne peut aboutir : il attend les résultats de la requête qui ne se termine pas.
J'ai demandé a mon petit camarade de tester la requête chez lui, ca a mis son processeur à 100% et il a eut du mal a reprendre la main => MEFIEZ VOUS

je vais tester avec une version plus ancienne de mysql en attendant

par Ripat » 27 avr. 2005, 12:56

Comme je le pressentais, sans les index c'est une catastrophe. Au bout de 50 minutes, j'ai du tuer le PID mysqld!

Vérifie tes index.
As-tu essayé:

Code : Tout sélectionner

EXPLAIN SELECT ..... JOIN ... ON WHERE ....
Le tableau retourné te montre si MySQL utilise effectivement tes index pour ta requête. S'il ne les utilise pas tu peux toujours essayer de le forcer avec un USE INDEX (ton_index) ou FORCE INDEX (ton_index) dans ta requête.

Tant qu'à faire (quoi qu'il y ai peu de chance que le problème se situe au niveau des types de colonnes) essaye aussi:

Code : Tout sélectionner

SELECT * FROM ... LEFT JOIN ... ON ... WHERE ... PROCEDURE ANALYSE()
Si le tableau retourné est vide, c'est tout bon. Sinon, change le type de colonne dans le type suggéré par MySQL

Après ça, je ne vois plus trop que faire...

par Ripat » 27 avr. 2005, 12:00

Je pense sincèrement que c un bug de mysql sur ma machine avec ma base sur mon ordi
Je le pense aussi. Je penche plutôt pour des index corrompus.

Je suppose que tu as déjà essayé de les reconstruire? Optimize table peut-être?

Je t'ai fait un test sur mes bases:

1- Ici je force un NULL sur la table jointe (clients.codcli est toujours différent de ventes.codart)

Code : Tout sélectionner

SELECT count(*) FROM clients LEFT JOIN ventes ON clients.codcli = ventes.codart WHERE ventes.codcli IS NULL
table ventes => 853038 (index sur codcli et codart)
table clients => 25120


count(*) => 25120 en 0.28 sec la première requête et 0.00 sec (!) dès la deuxième (normal, l'optimiseur MySQL a placé les index appropriés en mémoire)

2- clients.codcli non présents dans ventes

Code : Tout sélectionner

SELECT count(*) FROM clients LEFT JOIN ventes ON clients.codcli = ventes.codcli WHERE ventes.codcli IS NULL
count(*) => 5490 en 0.02 sec la première requête et 0.00 sec (!) dès la deuxième .

Pendant le déjeûner, je lance un essai sans les index mais ça risque d'être (très très) long. :wink:

par cerber » 27 avr. 2005, 10:41

oui en fait, je l'ai tenté, sur la vraie base et sur une base "light" où seule les colonnes nécéssaires à la jointure étaient présentes...
Je pense sincèrement que c un bug de mysql sur ma machine avec ma base sur mon ordi
je vais essayer de piquer un autre ordi pour faire le test

par Cyrano » 27 avr. 2005, 10:18

Notez bien que je ne suis certes pas le king of the SQL, mais une idée m'a traversé l'esprit: dans les cours de SQL que j'ai eu l'an dernier, on m'a signalé qu'on pouvait notablement accélérer les traitements en choisissant soigneusement les INDEX : peut-être indexer certaines colonnes des tables aiderait à réduire les délais ?

Tu l'as peut-être déjà fait remarque, mais comme ça n'était pas mentionné, je me suis dit que ça pouvait aider si peu que ce soit.

par cerber » 27 avr. 2005, 10:06

J'ai une appli comparable sur des cardinalités proches des tiennes. Ca ne devrait pas dépasser la seconde.
Tu l'as testé (si tu peux :| ?)
Je suppose également que tu n'as pas demandé l'affichage des résultats car 28.000 lignes à afficher, même en ligne de commande, ça prend du temps.:wink:
lol non, je te rassure, g commencé avec des count(*) mais là aussi ca me retournais rien. De toute facon, quelque soit la requête, je récupère un max de 3000 enregistrements (je groupe sur les codes produits :) )


pour Albat :
Non mais tu me prend pour qui :evil: ! lol j'ai dit que j'était passé de 120Mo à 40Mo
1. Fait
2. Pas la peine : c des tables de prod => relativement propres (enfin, la base access a été crée depuis la base AS400 de prod à laquelle je n'ai pas acces)
3. Fait
4. Fait
5. Fait
6. Quels résultats ? la requête n'aboutit pas !

par albat » 26 avr. 2005, 22:14

Rhâââ Lovely ! :langue:

par Cyrano » 26 avr. 2005, 22:00

Oui not'bon maître :agenouille:

par albat » 26 avr. 2005, 21:33

La base originale a été faite par mon boss qui n'y connais rien en SQL => avec MS access
Les coupables doivent être punis.
Il n'y a rien de sadique là-dedans. Ce n'est que Justice. :axe:

Et puis d'abord, appelle-moi "Monsieur le Président-Directeur Général" ! :langue: