[MySQL] Comment optimiser une requête pourtant simple ? [c'était moi]

Comment optimiser une requête pourtant simple ? [c'était moi] [MySQL] - SQL/NoSQL - Programmation

Marsh Posté le 09-04-2004 à 16:54:59    

Salut,
Ma requête ressemble à ça :

Code :
  1. SELECT * FROM TABLE1 T1,  TABLE2 T2 WHERE T1.ID = 7 OR T2.ID = 7


Elle me renvoie ce que je veux, mais en faisant explain je me rends compte que le type de la requête sur les deux tables est "index" (donc les deux index sont parcourus entièrement !).
Si je divise la requête, la type devient bien sur const (beaucoup mieux) :

Code :
  1. SELECT * FROM TABLE1 T1 WHERE T1.ID = 7;
  2. SELECT * FROM TABLE2 T2 WHERE T2.ID = 7;


J'ai essayé de faire une union, mais après vérification, il y a problème avec la clause LIMIT. Par exemple :

Code :
  1. SELECT * FROM TABLE1 T1 WHERE T1.ID = 7
  2. UNION
  3. SELECT * FROM TABLE2 T2 WHERE T2.ID = 7
  4. LIMIT 1


Ceci ne renvoie qu'un seul résultat, mais exécute la requête sur TABLE2 même si TABLE1 a déjà renvoyé un résultat. :sarcastic:
Bof, Bof...
 
Je ne comprends vraiment pas ces comportements. :heink:
QQ1 peut m'aider à comprendre ou à optimiser ?


Message édité par kalex le 10-04-2004 à 02:04:35
Reply

Marsh Posté le 09-04-2004 à 16:54:59   

Reply

Marsh Posté le 09-04-2004 à 18:08:25    

Euh... T'es au courant qu'aucune des trois requètes que tu montres ne donne les mêmes résultats?

Reply

Marsh Posté le 09-04-2004 à 18:33:41    

T'as essayé de comprendre au moins ?

Reply

Marsh Posté le 09-04-2004 à 18:38:49    

oui, mais toi as-tu seulement compris ce que tu veux...

Reply

Marsh Posté le 09-04-2004 à 18:45:50    

La raison de ces comparaisons, c'est de chercher la meilleure optimisation interne de MySQL, si tu n'as pas compris...
Et si tu comprends si bien ces fonctionnements, tu vas me dire pourquoi MySQL scanne l'intégralité des deux index pour la première requête ?
 
edit : c'est vrai que j'ai l'air agressif :o, sorry !


Message édité par kalex le 09-04-2004 à 19:59:39
Reply

Marsh Posté le 09-04-2004 à 18:50:59    

produit cartésien.

Reply

Marsh Posté le 09-04-2004 à 19:01:21    

kalex a écrit :


Et si tu comprends si bien ces fonctionnements, tu vas me dire pourquoi MySQL scanne l'intégralité des deux index pour la première requête ?


tu trouves pas que tu es un poil agressif?


---------------
IVG en france
Reply

Marsh Posté le 09-04-2004 à 19:03:49    

gizmo a écrit :

produit cartésien.

Je vois... :o
Alors comment faire ? En fait, je veux juste savoir s'il y a un ID de 7, soit dans TABLE1 soit dans TABLE2.

Reply

Marsh Posté le 09-04-2004 à 19:06:41    

uriel a écrit :


tu trouves pas que tu es un poil agressif?

Oui, mais les réponses trop laconiques ont le dont de m'énerver...
Quand on a la solution pourquoi ne pas la donner ?
 
Enfin, désolé. :o

Reply

Marsh Posté le 09-04-2004 à 19:08:36    

Et tu te fouts de connaitre les autres champs? tu veux juste savoir s'il existe? Dans ce cas, réponse 2.


Message édité par gizmo le 09-04-2004 à 19:09:03
Reply

Marsh Posté le 09-04-2004 à 19:08:36   

Reply

Marsh Posté le 09-04-2004 à 19:14:42    

Réponce 2 ?
Merci de tes efforts, mais que veux-tu dire ? (j'aimerais en être sûr plutôt ;))
 
J'aimerais faire ça en une seule requête, parce qu?en deux, je sais faire...

Reply

Marsh Posté le 09-04-2004 à 19:21:14    

je veux simplement dire que faire cela en une seule requète sans faire de jointure, et donc sans exploser ton temps de calcul, c'est simplement impossible.

Reply

Marsh Posté le 09-04-2004 à 19:29:01    

Merci.
Je vais encore t'emmerder avec une dernière question.
Si je fait :
SELECT * FROM TABLE1 T1,  TABLE2 T2 WHERE T1.ID = 7 OR T2.ID = 7 LIMIT 1
L'index est-il réellement passé en revue ? A priori non, puisqu'on s'arrête au premier résultat ? Le temps de calcul devrait rester très bon ?

Reply

Marsh Posté le 09-04-2004 à 20:28:08    

Non, le problème n'est pas là.
Quand tu fais une telle requète, voici ce qui se passe en interne:
 
- l'interpréteur voit qu'il y a deux tables, il va donc chercher à les joindre
- l'interpréteur cherche un critère de jointure, ici il n'y en a pas. Les n colonnes de T1 vont donc recevoir chacune m colonnes de T2, soit n*m.
- Dans cette nouvelle maxi-table, l'interpréteur cherche, ligne par ligne (ou en utilisant l'index s'il y en a un) à vérifier la clause where (or compris)
- l'interpréteur voit une ligne qui correspond à la demande et rempli un objet avec cette ligne
- l'interpréteur voit qu'on lui demande de s'arreter après un objet trouvé, il retourne cet objet.
 
Tu comprends?

Reply

Marsh Posté le 09-04-2004 à 20:30:17    

kalex a écrit :

Merci.
Je vais encore t'emmerder avec une dernière question.
Si je fait :
SELECT * FROM TABLE1 T1,  TABLE2 T2 WHERE T1.ID = 7 OR T2.ID = 7 LIMIT 1
L'index est-il réellement passé en revue ? A priori non, puisqu'on s'arrête au premier résultat ? Le temps de calcul devrait rester très bon ?

Tant qu'on y est :
SELECT 1 FROM TABLE1 T1,  TABLE2 T2 WHERE T1.ID = 7 OR T2.ID = 7 LIMIT 1
Ca ne change pas fondamentalement la complexité de la requête, mais il devrait ne s'occuper que des index, et pas du tout du reste de la table :)
 
Mais je pense que la solution en 2 requêtes est la meilleure :)


Message édité par mrbebert le 09-04-2004 à 20:32:36
Reply

Marsh Posté le 09-04-2004 à 20:34:30    

gizmo a écrit :

Non, le problème n'est pas là.
Quand tu fais une telle requète, voici ce qui se passe en interne:
 
- l'interpréteur voit qu'il y a deux tables, il va donc chercher à les joindre
- l'interpréteur cherche un critère de jointure, ici il n'y en a pas. Les n colonnes de T1 vont donc recevoir chacune m colonnes de T2, soit n*m.
- Dans cette nouvelle maxi-table, l'interpréteur cherche, ligne par ligne (ou en utilisant l'index s'il y en a un) à vérifier la clause where (or compris)
- l'interpréteur voit une ligne qui correspond à la demande et rempli un objet avec cette ligne
- l'interpréteur voit qu'on lui demande de s'arreter après un objet trouvé, il retourne cet objet.
 
Tu comprends?

Je comprends bien, c'est très clair, merci. :jap:
Juste un truc, pourquoi explain n'indique pas "Using temporary" lorsqu'il utilise ce mécanisme ?

Reply

Marsh Posté le 09-04-2004 à 20:36:20    

mrbebert a écrit :

Tant qu'on y est :
SELECT 1 FROM TABLE1 T1,  TABLE2 T2 WHERE T1.ID = 7 OR T2.ID = 7 LIMIT 1
Ca ne change pas fondamentalement la complexité de la requête, mais il devrait ne s'occuper que des index, et pas du tout du reste de la table :)

Ca, j'avais déjà compris ! ;)

Reply

Marsh Posté le 09-04-2004 à 20:38:43    

kalex a écrit :

Je comprends bien, c'est très clair, merci. :jap:
Juste un truc, pourquoi explain n'indique pas "Using temporary" lorsqu'il utilise ce mécanisme ?


le résultat de explain dépend d'un SGDB à l'autre. En général, ils indique qqch comme "merge" avec la condition de jointure, mais il faut encore qu'il y en aie une.

Reply

Marsh Posté le 09-04-2004 à 20:56:58    

Encore merci.
Cependant, je me demande toujours pourquoi UNION avec LIMIT 1 exécute la seconde requête si la première revoit une ligne.

Reply

Marsh Posté le 09-04-2004 à 21:07:28    

Parce que limit est la dernière des choses qui soit évalué.

Reply

Marsh Posté le 09-04-2004 à 21:18:55    

J'ai surement l'air d'un boulay.
Mais la base est "consciente" de la clause LIMIT #, puisqu'elle ne calcule et ne retourne que le nombre de ligne # ? Ce comportement est-il différent avec UNION ?

Reply

Marsh Posté le 09-04-2004 à 21:57:32    

Ce n'est pas évident que la base en tienne compte au moment où elle détermine comment elle va traiter la requête (ce qu'indique l'explain).
Peut être que le LIMIT n'intervient qu'après le début des traitements, lorsque le bon nombre de résultats à été trouvé [:figti]

Reply

Marsh Posté le 09-04-2004 à 22:10:11    

Code :
  1. SELECT @a := NOM FROM TABLE1 T1 WHERE T1.ID = 7
  2. UNION
  3. SELECT @b := NOM FROM TABLE2 T2 WHERE T2.ID = 7
  4. LIMIT 1


S'il y a une ligne de résultat pour T1, @a contiendra le NOM de cette ligne. Il n'y a donc qu'une ligne retournée, mais @b contient à chaque fois le NOM du premier résultat pour T2...
Comme tu dis [:figti]

Reply

Marsh Posté le 09-04-2004 à 22:18:49    

Effectivement, c'est curieux que @b ait une valeur :pt1cable:

Reply

Marsh Posté le 09-04-2004 à 23:35:08    

C'est peut-être un manque d'optimisation (une sorte de bug en fait !) de MySQL ?
Attendons gizmo qui a l'air calé. ;)

Reply

Marsh Posté le 10-04-2004 à 00:13:34    

quand je dis que limit est la dernière chose évaluée, dans ce cas, cela dépend si le SGDB est intelligent ou non.
 
Cas bête:  
 - il fait la première requète et stocke le tout dans une table temporaire
 - il fait la seconde requète et retourne donc un seul résultat (limit ne porte que sur celle-là) dans une seconde table temporaire
 - union est appliqué entre les deux tables temporaires pour virer les doublons
 
Cas intelligent:
 - la première requète est exécuté, le résultat blabla...
 - l'interpréteur remarque union et donc décide de rajouter les résultats non-doublon de la seconde requète directement dans la table temporaire
 -la seonde requète s'arrête après avoire trouvé un résultat

Reply

Marsh Posté le 10-04-2004 à 00:42:40    

Il ne s'agit, a priori, pas du cas bête puisque @a contient uniquement le nom de la première ligne (la seule retournée). S'il s'agissait du second cas UNION ALL devrait permettre de ne pas traiter les doublons ? Or le résultat est le même avec.
 
Pour moi le mécanisme logique serait :
- La base remarque LIMIT 1 et UNION ALL, elle sait donc que le 1er résultat trouvé sera suffisant,
- Elle exécute la premiere requête et reçoit un premier résultat,
- Elle s'arrête là.
A la rigueur, elle pourrait remarquer UNION ALL qu'après la réception du premier résultat...


Message édité par kalex le 10-04-2004 à 00:43:44
Reply

Marsh Posté le 10-04-2004 à 00:53:34    

kalex a écrit :

Il ne s'agit, a priori, pas du cas bête puisque @a contient uniquement le nom de la première ligne (la seule retournée).
cela ne veut rien dire. Dans les deux cas, limit ne touche absolument pas la première requète. La seule info que tu as, c'est qu'il n'y a effectivement qu'une seule ligne qui a un id=7 dans T1
 
S'il s'agissait du second cas UNION ALL devrait permettre de ne pas traiter les doublons ? Or le résultat est le même avec.
cela veut juste dire qu'il n'y a pas de ligne avec id=7 dans T2. Autrement, ton modèle est corrompu
 
Pour moi le mécanisme logique serait :
- La base remarque LIMIT 1 et UNION ALL, elle sait donc que le 1er résultat trouvé sera suffisant,
- Elle exécute la premiere requête et reçoit un premier résultat,
- Elle s'arrête là.
A la rigueur, elle pourrait remarquer UNION ALL qu'après la réception du premier résultat...
non, cela ne pourrait être vrai que si tes requètes étaient encapsulées dans des parenthéses, limit en dehors, ce qui n'est pas le cas

Reply

Marsh Posté le 10-04-2004 à 01:13:05    

Citation :

cela ne veut rien dire. Dans les deux cas, limit ne touche absolument pas la première requète. La seule info que tu as, c'est qu'il n'y a effectivement qu'une seule ligne qui a un id=7 dans T1


Justement non, s'il y en a plusieurs ça change rien (c'est plus des IDs bien sur) !
 
Changeons :

Code :
  1. SELECT @a := NOM FROM TABLE1 T1 WHERE T1.AGE = 7 /* Sans LIMIT, retourne plusieurs lignes. */
  2. UNION
  3. SELECT @b := NOM FROM TABLE2 T2 WHERE T2.AGE = 7 /* Sans LIMIT, retourne plusieurs lignes. */
  4. LIMIT 1

Ca ne retourne qu'une seule ligne (la 1ere de T1). Et les variables sont assignées comme précédemment.


Message édité par kalex le 10-04-2004 à 01:16:39
Reply

Marsh Posté le 10-04-2004 à 01:17:30    

Dans ce cas, je ne vois qu'une solution: MySQL est encore une fois en dehors de la norme standard SQL. Il faut se plonger dans leur doc pour voir comment ils gèrent les union.

Reply

Marsh Posté le 10-04-2004 à 01:20:45    

:cry:Si je trouve une soluce, je la donnerais ici.
 
Je te remercie vraiment beaucoup pour ta disponibilité gizmo. :jap:

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed