Mysql : requete avec EXISTS

Mysql : requete avec EXISTS - SQL/NoSQL - Programmation

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

Reply

Marsh Posté le 29-11-2006 à 11:30:19   

Reply

Marsh Posté le 29-11-2006 à 11:59:13    

Pourquoi tu passes par un exists, ta jointure suffit amplement

Code :
  1. SELECT A.*
  2. FROM vues V, annonces A
  3. WHERE annonces.id_annonce = vues.id_annonce

Reply

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;

Reply

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.

Reply

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  [:klem3i1]  
 

Reply

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.

Reply

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. :spamafote:


Message édité par MagicBuzz le 01-12-2006 à 11:51:44
Reply

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
Informix  select first 10 * from table
Microsoft SQL Server and Access  select top 10 * from table
MySQL and PostgreSQL  select * from table limit 10


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.


Message édité par anapajari le 01-12-2006 à 12:41:51
Reply

Marsh Posté le 01-12-2006 à 13:38:58    

Tiens petit test fait sur DB2:

Code :
  1. select A.id from table A where exists ( select B.id from B where A.id = B.id)


db2explain puis show optimized query donne

Code :
  1. select distinct A.id from A inner join B on A.id = B.id
 

C'est trop fou ça  [:hahaguy]

 

Donc ouais c'est vrai j'ai la tête dure ...


Message édité par anapajari le 01-12-2006 à 13:39:51
Reply

Marsh Posté le 01-12-2006 à 14:31:54    

Refais la meme chose en recuperant plus de donnes de la table A pour voir :)

Reply

Marsh Posté le 01-12-2006 à 14:31:54   

Reply

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.

Reply

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...

Reply

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)

Reply

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 :
  1. select * from dossiers where exists ( select fin_iddos from finan where fin_iddos=dos_id)


donne

Code :
  1. select
  2. ...
  3. la liste des 60 champs
  4. ...
  5. from dossiers, finan where ( dos_id = fin_iddos)


Mieux les plans d'executions sont les suivants:
jointure:

Optimizer Plan:
 
          RETURN
          (   1)
            |
          HSJOIN
          (   2)
         /      \
  TBSCAN          IXSCAN
  (   3)          (   4)
    |            /      \
 Table:    Index:     Table:
 DB2INST1  DB2INST1   DB2INST1
 DOSSIERS  FIN_IDDOS  FINAN


exists:


Optimizer Plan:
 
          RETURN
          (   1)
            |
          HSJOIN
          (   2)
         /      \
  TBSCAN          IXSCAN
  (   3)          (   4)
    |            /      \
 Table:    Index:     Table:
 DB2INST1  DB2INST1   DB2INST1
 DOSSIERS  FIN_IDDOS  FINAN


 
Alors je suis a 600% d'accord pour le union et le in, mais pour le exists toujours pas [:dawa]

Reply

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.

Reply

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:


* 272822 row(s) fetched, 5 row(s) output.

 

* Prepare Time is:       0,090023 seconds
* Execute Time is:       0,225449 seconds
* Fetch Time is:        17,107546 seconds
* Elapsed Time is:      17,423018 seconds (complete)

 

** The following warnings were issued:
** CLI warning in fetching next query result row:
(-99999): [IBM][CLI Driver] CLI0002W  Data truncated. SQLSTATE=01004

 

* Summary Table:

 

Type      Number      Repetitions Total Time (s) Min Time (s)   Max Time (s)   Arithmetic Mean Geometric Mean Row(s) Fetched Row(s) Output
--------- ----------- ----------- -------------- -------------- -------------- --------------- -------------- -------------- -------------
Statement           1           1      17,423018      17,423018      17,423018       17,423018      17,423018         272822             5

 

* Total Entries:              1
* Total Time:                17,423018 seconds
* Minimum Time:              17,423018 seconds
* Maximum Time:              17,423018 seconds
* Arithmetic Mean Time:      17,423018 seconds
* Geometric Mean Time:       17,423018 seconds
---------------------------------------------

  

jointure


* 537605 row(s) fetched, 5 row(s) output.

 

* Prepare Time is:       0,065094 seconds
* Execute Time is:       0,238727 seconds
* Fetch Time is:        16,685027 seconds
* Elapsed Time is:      16,988848 seconds (complete)

 

** The following warnings were issued:
** CLI warning in fetching next query result row:
(-99999): [IBM][CLI Driver] CLI0002W  Data truncated. SQLSTATE=01004

 

* Summary Table:

 

Type      Number      Repetitions Total Time (s) Min Time (s)   Max Time (s)   Arithmetic Mean Geometric Mean Row(s) Fetched Row(s) Output
--------- ----------- ----------- -------------- -------------- -------------- --------------- -------------- -------------- -------------
Statement           1           1      16,988848      16,988848      16,988848       16,988848      16,988848         537605             5

 

* Total Entries:              1
* Total Time:                16,988848 seconds
* Minimum Time:              16,988848 seconds
* Maximum Time:              16,988848 seconds
* Arithmetic Mean Time:      16,988848 seconds
* Geometric Mean Time:       16,988848 seconds
---------------------------------------------


[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 :o
[/mode honnête]


Message édité par anapajari le 01-12-2006 à 17:20:22
Reply

Marsh Posté le 01-12-2006 à 17:30:35    

:D

Reply

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 :D

Reply

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 :sweat:)

Reply

Sujets relatifs:

Leave a Replay

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