IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage SQL Discussion :

Sous-requête dans INNER JOIN vs IN


Sujet :

Langage SQL

  1. #1
    Membre habitué
    Inscrit en
    Avril 2003
    Messages
    397
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 397
    Points : 133
    Points
    133
    Par défaut Sous-requête dans INNER JOIN vs IN
    Bonjour,

    Jeu de test

    Table _produit
    id nom
    1 TV
    2 Voyage

    Table _vente
    id prix produit date
    1 500 TV 2000-01-01
    2 600 TV 2010-01-01
    3 1200 Voyage 2010-01-01
    4 900 Voyage 2000-01-01

    Objectif

    Afficher chaque produit avec son prix vente le plus cher et sa date.

    produit prix date
    TV 600 2010-01-01
    Voyage 1200 2010-01-01

    Deux solutions

    1/ Sous-requêtes dans INNER

    Celle-ci se comprend très bien, on imagine que la sous-requête est exécutée en 1er pour constituer une "table virtuelle" puis la jointure est réalisée sur deux colonnes.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    SELECT *
    FROM _produit p
    INNER JOIN _vente v1
    ON p.id = v1.produit
    INNER JOIN (
    	SELECT produit, max(prix) as prix
        FROM _vente v2
        GROUP BY produit
    ) as v
    ON p.id = v.produit AND v.prix = v1.prix
    2/ Sous-requête dans un IN

    Celle-ci fonctionne également mais se comprend difficilement... Comment le traitement est-il exécuté ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    SELECT *
    FROM _vente v1
    INNER JOIN _produit p1
    ON p1.id = v1.produit
    WHERE v1.prix IN (
    	SELECT max(v2.prix)
        FROM _vente v2
        WHERE p1.id = v2.produit
    )
    3/ Quelle est la requête la plus performante ?

    Merci

  2. #2
    Modérateur
    Avatar de al1_24
    Homme Profil pro
    Retraité
    Inscrit en
    Mai 2002
    Messages
    9 080
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2002
    Messages : 9 080
    Points : 30 803
    Points
    30 803
    Par défaut
    Citation Envoyé par dorian53 Voir le message
    3/ Quelle est la requête la plus performante ?
    Beaucoup d'éléments entrent en jeu pour donner la réponse à cette question :
    Quel est le nombre de lignes dans ces tables ?
    Y a-t-il un ou plusieurs index sur ces tables ?
    Y a-t-il des statistiques sur ces tables ? Sont-elles à jour ?
    Quelle est la capacité de l'optimiseur à identifier le meilleur plan d'exécution ?
    Modérateur Langage SQL
    Règles du forum Langage SQL à lire par tous, N'hésitez pas à consulter les cours SQL
    N'oubliez pas le bouton et pensez aux balises
    [code]
    Si une réponse vous a aidé à résoudre votre problème, n'oubliez pas de voter pour elle en cliquant sur
    Aide-toi et le forum t'aidera : Un problème exposé sans mentionner les tentatives de résolution infructueuses peut laisser supposer que le posteur attend qu'on fasse son travail à sa place... et ne donne pas envie d'y répondre.

  3. #3
    Membre habitué
    Inscrit en
    Avril 2003
    Messages
    397
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 397
    Points : 133
    Points
    133
    Par défaut
    Salut,

    Citation Envoyé par al1_24 Voir le message
    Beaucoup d'éléments entrent en jeu pour donner la réponse à cette question :
    Quel est le nombre de lignes dans ces tables ?
    Y a-t-il un ou plusieurs index sur ces tables ?
    Difficile de répondre précisément, c'est une version simplifiée d'une requête beaucoup plus complexe une 15aine d'INNER JOIN sur la requête parent.

    Citation Envoyé par al1_24 Voir le message
    Y a-t-il des statistiques sur ces tables ? Sont-elles à jour ?
    Quelle est la capacité de l'optimiseur à identifier le meilleur plan d'exécution ?
    Le SGBD utilisé est Percona 5.6, quelles statistiques puis-je remonter et où les trouver ?
    Comment juger de la capacité de l'optimiseur ?

    Saurais-tu expliquer la logique d’exécution de la sous-requête dans un IN ?

    Merci
    Images attachées Images attachées  

  4. #4
    Modérateur

    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 799
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 799
    Points : 34 031
    Points
    34 031
    Billets dans le blog
    14
    Par défaut
    À mon avis, le problème de la seconde requête est que la sous-requête dans le IN va être exécutée pour chaque identifiant de produit, alors que la première requête ne va exécuter qu'une fois la sous-requête de la jointure et procéder à la jointure sur le résultat de la sous-requête. La jointure étant une opération très optimisée dans les SGBD, la première requête me semble a priori plus efficace mais il faut voir le plan d'exécution de chaque requête. Ne connaissant pas Percona, je ne peux pas aider sur ce dernier point. Mais comme je vois que Percona semble basé sur MySQL, un EXPLAIN de la requête fonctionnera peut-être ?
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole. Autoentrepreneur.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

  5. #5
    Membre habitué
    Inscrit en
    Avril 2003
    Messages
    397
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 397
    Points : 133
    Points
    133
    Par défaut
    Citation Envoyé par CinePhil Voir le message
    À mon avis, le problème de la seconde requête est que la sous-requête dans le IN va être exécutée pour chaque identifiant de produit
    Oui, c'est ce que j'imagine également.

    Requête 1 via INNER JOIN

    Nom : Capture du 2017-09-22 10-15-23.png
Affichages : 2921
Taille : 15,9 Ko

    Requête 2 via IN

    Nom : Capture du 2017-09-22 10-11-45.png
Affichages : 2894
Taille : 12,5 Ko

  6. #6
    Membre habitué
    Inscrit en
    Avril 2003
    Messages
    397
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 397
    Points : 133
    Points
    133
    Par défaut
    Je viens de tester avec plus de 3 000 000 de lignes de vente.

    Requête 1 via INNER JOIN
    Répond en 15 secondes.

    Nom : téléchargement.png
Affichages : 3112
Taille : 23,2 Ko

    Requête 2 via IN
    Ne répond pas... timeout.

    Nom : téléchargement (1).png
Affichages : 2886
Taille : 17,8 Ko

    Vraiment curieux, l'EXPLAIN laisse penser le contraire en terme de performance.

    C'est "DEPENDANT SUBQUERY" qui doit correspondre à l'itération de chaque produit pour trouver le MAX.

  7. #7
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    Mai 2002
    Messages
    21 768
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Expert bases de données / SQL / MS SQL Server / Postgresql
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2002
    Messages : 21 768
    Points : 52 571
    Points
    52 571
    Billets dans le blog
    5
    Par défaut
    Citation Envoyé par dorian53 Voir le message
    Bonjour,

    Deux solutions

    1/ Sous-requêtes dans INNER

    Celle-ci se comprend très bien, on imagine que la sous-requête est exécutée en 1er pour constituer une "table virtuelle" puis la jointure est réalisée sur deux colonnes.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    SELECT *
    FROM _produit p
    INNER JOIN _vente v1
    ON p.id = v1.produit
    INNER JOIN (
    	SELECT produit, max(prix) as prix
        FROM _vente v2
        GROUP BY produit
    ) as v
    ON p.id = v.produit AND v.prix = v1.prix
    2/ Sous-requête dans un IN

    Celle-ci fonctionne également mais se comprend difficilement... Comment le traitement est-il exécuté ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    SELECT *
    FROM _vente v1
    INNER JOIN _produit p1
    ON p1.id = v1.produit
    WHERE v1.prix IN (
    	SELECT max(v2.prix)
        FROM _vente v2
        WHERE p1.id = v2.produit
    )
    3/ Quelle est la requête la plus performante ?

    Merci
    Ces deux requêtes ne sont absolument pas équivalentes !
    1) avec la requête JOIN et l'imbécile SELECT * vous vous retrouvez à avoir toutes les colonnes des 3 tables : _produit , _vente et la table dérivée aliassée v (colonnes aliassée produit et prix). Dans la IN, seules les colonnes des tables _produit et _vente y figure. Ceci a une influence sur les performances.
    2) avec l'imbécile SELECT *, vous faites en fait un SELECT ALL *, qui incorpore tous les doublons. Dans le cas de la jointure il peut donc y avoir plusieurs fois potentiellement la même ligne... Alors que dans le cas du IN, une seule ligne sera retournée. Ceci a aussi une influence sur les performances.

    Vos requêtes seraient équivalentes si vous les aviez écrites comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    SELECT DISTINCT P.*, v1.*
    FROM   _produit p
           INNER JOIN _vente v1 
                 ON p.id = v1.produit
           INNER JOIN (SELECT produit, max(prix) as prix
                       FROM   _vente v2
                       GROUP  BY produit) as v
                 ON p.id = v.produit AND v.prix = v1.prix;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    SELECT *
    FROM   _vente v1
           INNER JOIN _produit p1
                 ON p1.id = v1.produit
    WHERE  v1.prix IN (SELECT max(v2.prix)
                       FROM   _vente v2
                       WHERE p1.id = v2.produit);
    Sur un bon SGBDR, ces deux requêtes étant strictement mathématiquement équivalentes, il en résulterait un même plan d'exécution.

    Bref, apprenez le langage SQL... Mon livre peut vous y aider :
    Nom : Couverture SQL Synthex 4e ed - 500.jpg
Affichages : 2842
Taille : 77,8 Ko

    A +
    Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL
    Le site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/
    Blog SQL, SQL Server, SGBDR : http://blog.developpez.com/sqlpro
    Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp.
    Entreprise SQL SPOT : modélisation, conseils, audit, optimisation, formation...
    * * * * * Expertise SQL Server : http://mssqlserver.fr/ * * * * *

  8. #8
    Membre habitué
    Inscrit en
    Avril 2003
    Messages
    397
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 397
    Points : 133
    Points
    133
    Par défaut
    Salut,

    Merci pour ton retour.

    Il est vrai que le SELECT * influe sur les temps de réponse, dans ces exemples je n'ai pas pris la peine de préciser les champs me concentrant sur la structure différente des deux requêtes. Comme indiquée c'est une version simplifiée d'une requête beaucoup plus complexe gérée par un ORM.

    Bien vu pour le DISTNCT, s'il y a plusieurs vente du même produit, au même prix max à la même date.

    En tout cas, cela ne remet pas en cause l'énorme différence de performance entre les deux.

  9. #9
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    Mai 2002
    Messages
    21 768
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Expert bases de données / SQL / MS SQL Server / Postgresql
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2002
    Messages : 21 768
    Points : 52 571
    Points
    52 571
    Billets dans le blog
    5
    Par défaut
    Ce qui l'explique c'est le choix du SGBD qui est MySQmerde. Si vous étiez sous SQL Server par exemple, l'optimiseur aurait constaté que la demande est identique et l'algébriseur simplifié à même formule finale.
    Ce qui aurait conduit au même plan et même performances.

    Malheureusement MySQmerde est doté du plus mauvais optimiseur... Cela s'explique :
    • L'un coute rien, on en a donc pour son argent
    • l'autre coute plus cher et on en a aussi pour son argent !


    Conclusion, si vous voulez des performances, et pas récrire toutes vos requêtes, évitez certains SGBDR, MySQL en tête !

    A +
    Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL
    Le site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/
    Blog SQL, SQL Server, SGBDR : http://blog.developpez.com/sqlpro
    Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp.
    Entreprise SQL SPOT : modélisation, conseils, audit, optimisation, formation...
    * * * * * Expertise SQL Server : http://mssqlserver.fr/ * * * * *

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. SELECT, JOIN et sous-requêtes dans une même colonne
    Par Alcorak dans le forum Langage SQL
    Réponses: 2
    Dernier message: 09/06/2011, 09h04
  2. Sous requête dans Access
    Par ob1knob dans le forum Requêtes et SQL.
    Réponses: 1
    Dernier message: 10/08/2006, 17h30
  3. requête avec inner join ?
    Par Melvine dans le forum Oracle
    Réponses: 5
    Dernier message: 30/03/2006, 09h16
  4. Sous requêtes dans un SELECT ??
    Par solp dans le forum Langage SQL
    Réponses: 2
    Dernier message: 22/03/2006, 15h39
  5. Sous-requête dans la clause Select
    Par Danger dans le forum WinDev
    Réponses: 2
    Dernier message: 24/05/2005, 17h33

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo