Mysql : requete avec EXISTS - SQL/NoSQL - Programmation
Marsh Posté le 29-11-2006 à 11:59:13
Pourquoi tu passes par un exists, ta jointure suffit amplement
Code :
|
Marsh Posté le 30-11-2006 à 22:43:39
Il veut retourner les 30 premières annonces, or il peut y avoir plusieurs photos par annonce, d'où le passage (logique) par EXISTS.
SELECT *
FROM annonces
WHERE
EXISTS (SELECT *
FROM vues
WHERE vues.id_annonce = annonces.id_annonce)
LIMIT 0 , 30;
Marsh Posté le 30-11-2006 à 23:29:51
à la limite la requête d'anapajari marche si on colle un distinct, mais ce sera plus lent.
Marsh Posté le 01-12-2006 à 08:52:44
Beegee a écrit : Il veut retourner les 30 premières annonces, or il peut y avoir plusieurs photos par annonce, d'où le passage (logique) par EXISTS. |
Nan nan pas logique ....
MagicBuzz a écrit : à la limite la requête d'anapajari marche si on colle un distinct, mais ce sera plus lent. |
Pas vrai, ça dépend à la fois des sgbd et des index.
Mais de toute façon vu c'est excatement la même requete que dans le exists ça peut pas être plus long
Marsh Posté le 01-12-2006 à 10:14:17
Clair que ça dépend des index et du SGBD ... perso je trouve ça plus lisible avec le EXISTS (que de faire la jointure puis un DISTINCT).
Au passage, ça va plus ou moins retourner toujours les mêmes annonces ... faut faire un random si on veut des annonces au pif mais ayant des photos.
Marsh Posté le 01-12-2006 à 11:48:34
anapajari > pas vrai pour le coup du "ça dépends des index et toussa".
la seule chose dont va dépendre la vitesse d'un DISTINCT comparé à un EXISTS, c'est que le DISTINCT va prendre de plus en plus de temps (de façon plus ou moins exponentielle) quand le nombre de lignes va grandir, alors que le EXISTS va continuer à tourner avec la même vitesse (à peu de chose près, c'est plus évolution logarythmique).
Je rappelle quand même (une fois plus, y'en a, même parmis les habitués, genre anapajari, qui ont la tête dure) que le EXISTS ne fait que tester la validité d'une jointure. Il est de notoriété publique qu'une jointure ça ne consomme rien ou presque, même sur un SGBD merdique comme Access ou MySQL.
L'intérêt d'une jointure couplée à un LIMIT, si on ne spécifie rien d'autre (pas de distinct, group by, order by ou having) c'est que le SGBD ne va effectivement prendre que les 30 premières lignes. A partir du moment où on a un DISTINCT par exemple, le SGBD doit lire plus de lignes que demandé par le LIMIT, car il devra enlever les doublons. Ca peut rapidement devenir très lent.
En gros, la requête proposée par Beegee, et j'abonde dans son sens fait :
1 jointure
lecture des 30 premières lignes première lignes de la table principale
AUCUNE lecture des données dans la table liée
Le distinct, lui, pour fonctionner, doit comparer chaque tuple retourné avec les précédents tuples retournés. Il en découle qu'au fur et à mesure que le nombre de lignes évolue, ça devient catastrophique.
Mais surtout, la requête prônée à tord par Anapajari fait :
1 jointure
lecture de toutes les lignes de la table principale et de la table liée
recheche des 30 premiers tuples uniques parmis les champs sélectionnés
Bref, pas besoin de bencher pour voir que c'est plus lent qu'avec le EXISTS : à la base, le EXISTS fait moins de travail.
Marsh Posté le 01-12-2006 à 12:41:16
I'm pas d'accord...
D'abord le "limit" comme tu dis est pas implémenté de la même façon sur tous les sgbd, d'ailleurs même la syntaxe n'est pas identique:
DB2 select * from table fetch first 10 rows only |
Donc ta phrase:
Citation : L'intérêt d'une jointure couplée à un LIMIT, si on ne spécifie rien d'autre (pas de distinct, group by, order by ou having) c'est que le SGBD ne va effectivement prendre que les 30 premières lignes. |
est vrai dans le cas d'un limit mais pas d'un fetch first par exemple.
Marsh Posté le 01-12-2006 à 13:38:58
Tiens petit test fait sur DB2:
Code :
|
db2explain puis show optimized query donne
Code :
|
C'est trop fou ça
Donc ouais c'est vrai j'ai la tête dure ...
Marsh Posté le 01-12-2006 à 14:31:54
Refais la meme chose en recuperant plus de donnes de la table A pour voir
Marsh Posté le 01-12-2006 à 16:05:01
c clair que moi j'attends le résultat de l'une et l'autre avec un jeu de données important.
moi je ne crois pas un quart de seconde qu'un DISTINCT puisse être "optimized".
ça et le UNION (sans "ALL" ) ainsi que le IN, ce sont les trois instructions à éviter comme la peste.
n'importe quelle doc de n'importe quel SGBD le dit.
moi j'invente rien, et c'est 10 ans d'expérience qui parlent.
Marsh Posté le 01-12-2006 à 16:28:56
a noter aussi (j'avais pas fait gaffe) que tu récupère l'ID de la table A.
c'est cool, mais forcément, un DISTINCT sur une un PK, c'est pas vraiment pareil que sur un champ (même indexé).
refais-nous la requête avec un distinct sur un autre champ (non unique) de la table A...
Marsh Posté le 01-12-2006 à 16:30:30
en plus ton exemple est bancal, la FK de B vers A est aussi la PK de B... au lieu de faire une jointure de cardinalité 0,n, tu fais une cardinalité 0,1, donc le distinct est inutile (et certainement ignoré par l'optimiseur)
Marsh Posté le 01-12-2006 à 16:48:59
Ok the chiffres:
table dossiers: 277404 records
table finan: 537672
le explain et optimized query de
Code :
|
donne
Code :
|
Mieux les plans d'executions sont les suivants:
jointure:
Optimizer Plan: |
exists:
|
Alors je suis a 600% d'accord pour le union et le in, mais pour le exists toujours pas
Marsh Posté le 01-12-2006 à 16:54:14
déjà, y'a pas la moitié des infos : par rapport à ta requête à toi ?
ensuite, ça donne quoi niveau résultat (temps d'exécution)
parceque le plan, c'est joli, mais parfois on a des surprises
sinon, à vue de nez, t'as juste l'air de nous sortir qu'au mieux, c'est équivalent... alors autant utiliser le EXISTS qui veut dire ce qu'il veut dire, plutôt qu'un DISTINCT et une jointure qui risque de foutre la merde le jour où on veut modifier la requête.
Marsh Posté le 01-12-2006 à 17:19:52
Entièrement d'accord avec le fait que le exists est plus clair à lire dans ce cas-ci.
Alors oui les résultats sont equivalents et voila le score:
exits:
* Prepare Time is: 0,090023 seconds ** The following warnings were issued: * Summary Table: Type Number Repetitions Total Time (s) Min Time (s) Max Time (s) Arithmetic Mean Geometric Mean Row(s) Fetched Row(s) Output * Total Entries: 1 |
jointure
* Prepare Time is: 0,065094 seconds ** The following warnings were issued: * Summary Table: Type Number Repetitions Total Time (s) Min Time (s) Max Time (s) Arithmetic Mean Geometric Mean Row(s) Fetched Row(s) Output * Total Entries: 1 |
[mode honnête]
Par contre j'ai également fait le test sur une autre table ou les indexs sont merdeux et dans cas là, le fetch de la jointure est en gros deux fois celui du exists
[/mode honnête]
Marsh Posté le 02-12-2006 à 00:06:29
Et puis vaut mieux lancer chaque requête 2 fois, ou vider le cache avant chaque requête ... parce que sinon la 2ème requête profite toujours de la mise en cache de certaines données dû au lancement de la 1ère requête
Marsh Posté le 02-12-2006 à 00:17:08
Bah ça, ça dépends du SGBD et de la charge mémoire par contre.
Sous Oracle par exemple, tu peux lancer une pure requête de merde qui va prendre 5 minutes, tu peux être sûr que si tu es seul sur le serveur et que tu redemandes plusieurs minutes plus tard,y va te sortir des temps à la con genre 2ms (c'est relou à souhait pour débuguer une requête qui est lente... t'es obligé de ruser à lui gavant le cache avec des requêtes bidons entre chaque test )
Marsh Posté le 29-11-2006 à 11:30:19
Bonjour,
Voici ma requete qui doit afficher les annonces comprenant des photos. Le problème est que cette requete affiche aussi les annonces qui n'en n'ont pas...
Alors que SELECT * FROM vues, annonces WHERE annonces.id_annonce = vues.id_annonce affiche toutes les annonces qui ont des photos.
Je pense que ça vient de la clause EXISTS mais je ne vois pas pourquoi.
SELECT *
FROM annonces
WHERE
EXISTS (
SELECT *
FROM vues, annonces
WHERE annonces.id_annonce = vues.id_annonce
)
LIMIT 0 , 30