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 :

Quelle est la requête SQL qui détermine le produit non acheté


Sujet :

Langage SQL

  1. #1
    Nouveau Candidat au Club
    Inscrit en
    Septembre 2005
    Messages
    4
    Détails du profil
    Informations forums :
    Inscription : Septembre 2005
    Messages : 4
    Points : 0
    Points
    0
    Par défaut Quelle est la requête SQL qui détermine le produit non acheté
    Bonjour
    J’ai deux tables

    Produit (id,nom_produit)
    Achat(id,id_produit)

    Quelle est la requête SQL qui détermine le produit non acheté ?

    Exemple

    Produit (id,nom_produit)

    1-chocolat
    2-banane
    3-bonbon
    4-pain


    Achat (id, id_produit)

    1-2
    2-1
    3-1
    4-2
    5-4

    Résultat
    Produit.id=3 (produit 3 non acheté)

    Cordialement

  2. #2
    Modérateur
    Avatar de sevyc64
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2007
    Messages
    10 193
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 193
    Points : 28 077
    Points
    28 077
    Par défaut
    Deux solutions, soit tu utilise une sous requete dans le filtre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    SELECT * FROM PRODUIT
    WHERE ID NOT IN (SELECT DISTINCT ID_PRODUIT FROM ACHAT)
    Soit tu utilise l'opérateur ensembliste EXCEPT si ton SGBd le supporte :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    SELECT * FROM PRODUIT
    EXCEPT
    SELECT P.* FROM PRODUIT P
    INNER JOIN ACHAT A ON P.ID=A.ID_PRODUIT
    --- Sevyc64 ---

    Parce que le partage est notre force, la connaissance sera notre victoire

  3. #3
    Nouveau Candidat au Club
    Inscrit en
    Septembre 2005
    Messages
    4
    Détails du profil
    Informations forums :
    Inscription : Septembre 2005
    Messages : 4
    Points : 0
    Points
    0
    Par défaut remercimum
    bien marché merci sevyc64

  4. #4
    Expert éminent sénior

    Avatar de François DORIN
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Juillet 2016
    Messages
    2 757
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Charente Maritime (Poitou Charente)

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

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 757
    Points : 10 541
    Points
    10 541
    Billets dans le blog
    21
    Par défaut
    Bonjour,

    Il existe une troisième solution, à base de jointure gauche :
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    SELECT P.* FROM PRODUIT AS P
    LEFT JOIN ACHAT AS A ON A.produit_id = P.id
    WHERE A.id IS NULL
    François DORIN
    Consultant informatique : conception, modélisation, développement (C#/.Net et SQL Server)
    Site internet | Profils Viadéo & LinkedIn
    ---------
    Page de cours : fdorin.developpez.com
    ---------
    N'oubliez pas de consulter la FAQ C# ainsi que les cours et tutoriels

  5. #5
    Modérateur
    Avatar de sevyc64
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2007
    Messages
    10 193
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 193
    Points : 28 077
    Points
    28 077
    Par défaut
    Exact.

    Reste à voir le plan d’exécution. Peut-être un peu moins performant, ou pas, je sais pas.
    --- Sevyc64 ---

    Parce que le partage est notre force, la connaissance sera notre victoire

  6. #6
    Modérateur
    Avatar de escartefigue
    Homme Profil pro
    bourreau
    Inscrit en
    Mars 2010
    Messages
    10 136
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 136
    Points : 38 909
    Points
    38 909
    Billets dans le blog
    9
    Par défaut
    Il existe une autre solution, souvent la plus performante avec la solution jointure outer proposée par Dorinf :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT *
    FROM PRODUIT AS MAIN
    WHERE NOT EXISTS
         (SELECT 1
          FROM ACHAT AS SUBQ
          WHERE SUBQ.ID_PRODUIT = MAIN.ID)
    Un test d'existence ne devrait jamais utiliser WHERE (NOT) IN (subselect) car cette solution passe par la construction d'une table +/- volumineuse correspondant à la requête imbriquée alors qu'un test d'existence ne construit pas de table et ne transporte qu'un booléen

  7. #7
    Modérateur
    Avatar de sevyc64
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2007
    Messages
    10 193
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 193
    Points : 28 077
    Points
    28 077
    Par défaut
    Certes (le NOT IN est très souvent mal aimé).

    Je vais peut-être dire une bêtise, mais il me semble que dans le cas du NOT IN, la sous-requête sera exécutée une seule fois pour construire la liste, tandis que dans le cas du EXISTS, la sous-requête sera exécutée pour chacun des enregistrements de la requête principale, non ?
    --- Sevyc64 ---

    Parce que le partage est notre force, la connaissance sera notre victoire

  8. #8
    Modérateur
    Avatar de escartefigue
    Homme Profil pro
    bourreau
    Inscrit en
    Mars 2010
    Messages
    10 136
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 136
    Points : 38 909
    Points
    38 909
    Billets dans le blog
    9
    Par défaut
    L'ensemble n'est construit qu'une fois s'il s'agit d'une liste fermée avec des valeurs en dur, pas dans le cas d'un subselect

  9. #9
    Modérateur
    Avatar de Waldar
    Homme Profil pro
    Customer Success Manager @Vertica
    Inscrit en
    Septembre 2008
    Messages
    8 452
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Customer Success Manager @Vertica
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2008
    Messages : 8 452
    Points : 17 820
    Points
    17 820
    Par défaut
    Pour la petite histoire, Oracle DB réécrit les trois requêtes NOT IN, NOT EXISTS et LEFT JOIN IS NULL à peu de chose près de la même façon, comme ça ce débat n'a plus lieu d'exister.

    Une note toutefois avec NOT IN, la gestion des nulls.
    Si on rajoute une ligne dans achat (6, null), la solution NOT IN ne fonctionne plus.

    À noter également qu'écrire un DISTINCT dans une sous-requête IN / NOT IN est inutile.
    Si le SGBD est bon il l'ignore, sinon il perd du temps inutilement.

  10. #10
    Modérateur
    Avatar de escartefigue
    Homme Profil pro
    bourreau
    Inscrit en
    Mars 2010
    Messages
    10 136
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 136
    Points : 38 909
    Points
    38 909
    Billets dans le blog
    9
    Par défaut
    Sans doute, mais rien n'indique qu'ici il s'agisse d'Oracle et il est préférable de choisir la solution la plus adéquate, certains SGBD ne disposant pas d'un optimiseur très performant, et ça rend la solution portable à moindre frais

  11. #11
    Expert éminent sénior
    Homme Profil pro
    Responsable Données
    Inscrit en
    Janvier 2009
    Messages
    5 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Responsable Données

    Informations forums :
    Inscription : Janvier 2009
    Messages : 5 198
    Points : 12 774
    Points
    12 774
    Par défaut
    Bonjour,
    Pour donner de l'eau au moulin, je viens de faire le test avec MaxDb avec les 3 versions de la requête, et j'ai 3 plans d'exécution différents:
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    select * from ent_cde_frn
    where frn_c_code not in (select frn_c_code from fournisseur)
    OWNER TABLENAME COLUMN_OR_INDEX STRATEGY
    TORPEDO FOURNISSEUR TABLE SCAN
    TORPEDO ENT_CDE_FRN TABLE SCAN
    QUERYREWRITE - APPLIED RULES:
    DistinctPullUp 2
    DistinctForSubqueries 1
    DistinctPushDownTo 1
    DistinctPushDownFrom 1
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    explain
    select ent_cde_frn.*
    from ent_cde_frn
    left outer join fournisseur on fournisseur.frn_c_code = ent_cde_frn.frn_c_code
    where fournisseur.frn_c_code is null
    OWNER TABLENAME COLUMN_OR_INDEX STRATEGY
    TORPEDO ENT_CDE_FRN TABLE SCAN
    TORPEDO FOURNISSEUR FRN_C_CODE JOIN VIA KEY COLUMN
    TABLE HASHED
    NO TEMPORARY RESULTS CREATED
    RESULT IS COPIED
    Et pour finir:
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    explain
    select ent_cde_frn.*
    from ent_cde_frn
    where not exists(select 1 from fournisseur where fournisseur.frn_c_code = ent_cde_frn.frn_c_code)
    OWNER TABLENAME COLUMN_OR_INDEX STRATEGY
    TORPEDO ENT_CDE_FRN TABLE SCAN
    INTERNAL TEMPORARY RESULT EQUAL CONDITION FOR KEY
    SOU_C_CODE (USED KEY COLUMN)
    TORPEDO FOURNISSEUR EQUAL CONDITION FOR KEY
    FRN_C_CODE (USED KEY COLUMN)
    RESULT IS COPIED
    QUERYREWRITE - APPLIED RULES:
    DistinctPullUp 2
    DistinctForSubqueries 1
    DistinctPushDownTo 1
    DistinctPushDownFrom 1
    PushDownProjection 1

    On peut voir un TableScan de la table fournisseur dans le premier cas, alors que pour les deux autres une jointure est faite sur la colonne FRN_C_CODE (qui est la clé primaire de la table fournisseur).

    Ce serait pas mal de faire le même test avec d'autres SGBDs, histoire de comparer leurs comportements.

    Tatayo.

  12. #12
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    Mai 2002
    Messages
    21 769
    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 769
    Points : 52 720
    Points
    52 720
    Billets dans le blog
    5
    Par défaut
    Pour info c'est un exercice que je donne en cours SQL avancé avec SQL Server chez Orsys (cours PAV). Pour info, les 3 requêtes EXCEPT, NOT IN et NOT EXISTS donnent le même plan construit à base de semi anti jointure gauche...
    Mais récrite avec la semi anti jointure gauche cela donne un nouveau plan moins bon !!!

    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/ * * * * *

  13. #13
    Membre du Club
    Homme Profil pro
    Analyste d'exploitation
    Inscrit en
    Octobre 2016
    Messages
    144
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Analyste d'exploitation
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2016
    Messages : 144
    Points : 62
    Points
    62
    Par défaut
    pour l'instruction not exists(select ******)

    Est ce que c'est vrai que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    where not exists(select 1  from  ****
    est plus performant que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     where not exists(select *  from  ****
    qu méme celle du
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    where not exists(select NULL  from  ****

  14. #14
    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 801
    Points
    30 801
    Par défaut
    Les bons SGBD ne tiennent pas compte du contenu de la clause SELECT dans la sous-requête utilisée par l'opérateur EXISTS et s'arrêteront à la première ligne trouvée.
    D'autres iront exécuter l'intégralité de la sous-requête
    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.

  15. #15
    Membre actif

    Inscrit en
    Mai 2010
    Messages
    401
    Détails du profil
    Informations forums :
    Inscription : Mai 2010
    Messages : 401
    Points : 294
    Points
    294
    Par défaut
    Citation Envoyé par dorinf Voir le message
    Bonjour,

    Il existe une troisième solution, à base de jointure gauche :
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    SELECT P.* FROM PRODUIT AS P
    LEFT JOIN ACHAT AS A ON A.produit_id = P.id
    WHERE A.id IS NULL
    Merci beaucoup.

  16. #16
    Modérateur
    Avatar de escartefigue
    Homme Profil pro
    bourreau
    Inscrit en
    Mars 2010
    Messages
    10 136
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 136
    Points : 38 909
    Points
    38 909
    Billets dans le blog
    9
    Par défaut
    Citation Envoyé par tatayo Voir le message
    Ce serait pas mal de faire le même test avec d'autres SGBDs, histoire de comparer leurs comportements
    J'avais conservé cette judicieuse remarque sous le coude, en attendant d'avoir l'occasion de prototyper sur du DB2, et voici ce qu'il en est
    contexte : TAB1 possède environ 30 000 lignes et TAB2 200 000, toutes deux ont un index primaire sur la colonne de jointure, les stats sont à jour

    Les 4 requêtés comparées sont
    1. NOT IN (subselect)
    2. NOT IN (subselect avec distinct)
    3. LEFT OUTER JOIN et test de nullité
    4. NOT EXISTS (subselect)


    Voici ce que donne l'explain :
    Pièce jointe 225497
    On constate que les 2 premières requêtes utilisent exactement le même plan et que, contrairement aux 2 autres elles requièrent un tri supplémentaire (method=3) sur la table la plus volumineuse
    Les requêtes 3 et 4 utilisent la même stratégie, avec des couts estimés plus favorables pour la solution 3 c'est à dire la jointure outer (CPU_MS = millisecondes, CPU_SU=service units)

    Cependant, un explain n'est pas une science exacte en particulier les couts estimés qui sont rarement fiables.
    Voici donc ce que donne l'exécution des 4 requètes, en temps et service units réellement consommés et là il n'y a pas photo :
    Pièce jointe 225498
    L'écart MS est non mesurable compte tenu des volumes trop faibles, par contre l'écart service unit est très significatif : on passe de 48000 (exists) à 100 000 (outer) et 176 000 (not in) soit 4 fois plus pour la solution NOT IN !

Discussions similaires

  1. [AC-2003] Requête SQL qui me dis aucun enregistrement alors que c'est faux..
    Par jossdu39 dans le forum Requêtes et SQL.
    Réponses: 1
    Dernier message: 13/11/2015, 10h22
  2. Quelle est la requête SQL ?
    Par salmahanae dans le forum Langage SQL
    Réponses: 5
    Dernier message: 16/10/2013, 10h28
  3. Requête SQL qui ne renvoie rien quand la valeur est nulle
    Par vocal94130 dans le forum Requêtes
    Réponses: 4
    Dernier message: 01/09/2010, 14h11
  4. Requête SQL qui a l'air simple mais qui est musclée !
    Par tamiii dans le forum Langage SQL
    Réponses: 5
    Dernier message: 04/07/2008, 16h51
  5. Requête SQL qui me bloque
    Par David Guillouet dans le forum Langage SQL
    Réponses: 5
    Dernier message: 27/04/2004, 14h52

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