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 :

[Puristes norme SQL] UNION / UNION ALL


Sujet :

Langage SQL

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre émérite
    Homme Profil pro
    Inscrit en
    Mars 2002
    Messages
    899
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations forums :
    Inscription : Mars 2002
    Messages : 899
    Par défaut [Puristes norme SQL] UNION / UNION ALL
    Bonjour.

    J'aimerai refaire un point avec les habitués du SQL et de la théorie des ensembles.

    J'ai actuellement un problème avec mon moteur de base de données. (HyperFile)

    Je vais d'abord poser le contexte :


    Jusqu'ici pas de problème, tout est cohérent.
    Mon problème maintenant :
    La requête
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    SELECT FactureLigne_Qte20, FactureLigne_PxNetHT FROM FactureLigne
    UNION SELECT FactureLigne_Qte20, FactureLigne_PxNetHT FROM _ArcFactureLigne
    devrait pour moi renvoyer 316 lignes.
    C'est à dire :
    • Les lignes de FactureLigne (316 enreg)
    • Auquelles on ajoute les lignes de _ArcFactureLigne (0 enreg)
    • et à cet ensemble de données résultat, on retire l'intersection de FactureLigne et _ArcFactureLigne (soit 0 lignes, pour ne pas les prendre deux fois)

    Ce qui me chagrine, c'est que le moteur ne me renvoie que 256 lignes, soit le résultat de la requête suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    SELECT DISTINCT * FROM (
    SELECT FactureLigne_Qte20, FactureLigne_PxNetHT FROM FactureLigne
    UNION ALL SELECT FactureLigne_Qte20, FactureLigne_PxNetHT FROM _ArcFactureLigne
    )
    Donc voici ma question :
    Qui a raison ?

  2. #2
    Membre émérite Avatar de Oishiiii
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2009
    Messages
    508
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Administrateur de base de données

    Informations forums :
    Inscription : Août 2009
    Messages : 508
    Par défaut
    Bonjour,

    Je cite Wikipédia, sur l'opération d'union dans la théorie des ensembles:
    Dans la théorie des ensembles, l'union ou réunion de deux ensembles A et B est l'ensemble qui contient tous les éléments qui appartiennent à A ou appartiennent à B.
    Dans la théorie relationnelle (qui n'est pas la théorie SQL) les opérandes de l'opérateur UNION sont des relations. Une relation est un ensemble. Tout va bien.


    Le fait est que le langage SQL permet de manipuler des tables (ou relation) qui contiennent des doublons !
    Un ensemble par définition ne peux pas contenir de doublons ! Il n'y a pas de doublons dans l'ensemble des entiers naturels.

    Par défaut en SQL, l'opérateur SELECT permet donc de retourner des doublons. C'est une énorme erreur, un délit.
    C'est comme si, lorsqu'on écrit simplement "SELECT ..." on utilisait implicitement:
    L'opérateur UNION agit en respectant la théorie relationnel, car on est certain que le résultat d'une UNION de deux tables ne retournera jamais de doublons. C'est tout à fait correct.

    Citation Envoyé par Bowen Voir le message
    Ce qui me chagrine, c'est que le moteur ne me renvoie que 256 lignes, soit le résultat de la requête suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    SELECT DISTINCT * FROM (
    SELECT FactureLigne_Qte20, FactureLigne_PxNetHT FROM FactureLigne
    UNION ALL SELECT FactureLigne_Qte20, FactureLigne_PxNetHT FROM _ArcFactureLigne
    )
    Je dirais plutôt que le moteur retourne ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    SELECT DISTINCT FactureLigne_Qte20, FactureLigne_PxNetHT FROM FactureLigne
    UNION 
    SELECT DISTINCT FactureLigne_Qte20, FactureLigne_PxNetHT FROM _ArcFactureLigne
    Le "SELECT ALL" implicite a était remplacé par un "SELECT DISTINCT", ce qui devrait être le cas en permanence...


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT FactureLigne_Qte20, FactureLigne_PxNetHT FROM FactureLigne
    De toute évidence le résultat de cette requête (qui est une table en soit), d'après ce que vous nous dites, contient des doublons (60 lignes).

    Soit vous avez besoins de ces doublons dans le résultat de l'opération d'union qui comporte cette table en opérande, dans ce cas utilisez UNION ALL, soit vous n'en avez pas besoin, et il est tout à fait normal qu'UNION ne retourne pas ces doublons.

    Citation Envoyé par Bowen Voir le message
    Donc voici ma question :
    Qui a raison ?
    J'espère avoir était clair et avoir répondu à la question.

  3. #3
    Membre émérite
    Homme Profil pro
    Inscrit en
    Mars 2002
    Messages
    899
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations forums :
    Inscription : Mars 2002
    Messages : 899
    Par défaut
    Citation Envoyé par Oishiiii Voir le message
    Je dirais plutôt que le moteur retourne ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    SELECT DISTINCT FactureLigne_Qte20, FactureLigne_PxNetHT FROM FactureLigne
    UNION 
    SELECT DISTINCT FactureLigne_Qte20, FactureLigne_PxNetHT FROM _ArcFactureLigne
    Le "SELECT ALL" implicite a était remplacé par un "SELECT DISTINCT", ce qui devrait être le cas en permanence...
    Test effectué, le DISTINCT s'effectue bien sur le résultat final, pas sur chaune des requêtes.

    Puis-je prendre un jeu de données, pour faciliter ma compréhension ?

    FactureLigne : 4 enreg [ Qte20 | PxNetHT ]
    • 3 | 62
    • 6 | 17
    • 6 | 35
    • 6 | 17

    _ArcFactureLigne : 4 enreg [ Qte20 | PxNetHT ]
    • 8 | 22
    • 9 | 23
    • 6 | 35
    • 8 | 22


    J'ai donc 4 lignes, mais 3 valeurs distinctes par table.
    Et la ligne [ 6 | 35 ] se retrouve dans chacun des deux ensembles.

    La requête
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    SELECT DISTINCT FactureLigne_Qte20, FactureLigne_PxNetHT FROM FactureLigne
    UNION 
    SELECT DISTINCT FactureLigne_Qte20, FactureLigne_PxNetHT FROM _ArcFactureLigne
    ne me renvoie pourtant que 5 lignes. La ligne [ 6 | 35 ] n'apparaissant qu'une seule fois.

    La même requête sans les DISTINCT renvoie d'ailleurs exactement les mêmes lignes.
    Est-ce que mon raisonnement est mauvais?
    Pour moi, la requête
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    SELECT FactureLigne_Qte20, FactureLigne_PxNetHT FROM FactureLigne
    UNION 
    SELECT FactureLigne_Qte20, FactureLigne_PxNetHT FROM _ArcFactureLigne
    • 3 | 62
    • 6 | 17
    • 6 | 35
    • 6 | 17
    • 8 | 22
    • 9 | 23
    • 8 | 22

    Dans le cas contraire, la méthode pour récupérer ce résultat doit-elle être ?
    • Prendre l'ensemble de FactureLigne qui n'est pas dans _ArcFactureLigne (avec un NOT IN ou un NOT EXISTS)
    • Prendre l'ensemble de _ArcFactureLigne
    • Faire un UNION ALL des deux

  4. #4
    Membre émérite Avatar de Oishiiii
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2009
    Messages
    508
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Administrateur de base de données

    Informations forums :
    Inscription : Août 2009
    Messages : 508
    Par défaut
    Citation Envoyé par Bowen Voir le message
    • 3 | 62
    • 6 | 17
    • 6 | 35
    • 6 | 17
    • 8 | 22
    • 9 | 23
    • 8 | 22

    Dans le cas contraire, la méthode pour récupérer ce résultat doit-elle être ?
    • Prendre l'ensemble de FactureLigne qui n'est pas dans _ArcFactureLigne (avec un NOT IN ou un NOT EXISTS)
    • Prendre l'ensemble de _ArcFactureLigne
    • Faire un UNION ALL des deux
    Je ne comprend pas vraiment l'intérêt de la chose, mais oui, si c'est ce résultat que vous cherchez, effectivement il faut utiliser NOT EXISTS avec forcément UNION ALL.

    Sur SQL Server 2005 :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    WITH A(W, X) AS (
    	SELECT 3, 62 UNION ALL
    	SELECT 6, 17 UNION ALL
    	SELECT 6, 35 UNION ALL
    	SELECT 6, 17
    ), B (Y, Z) AS (
    	SELECT 8, 22 UNION ALL
    	SELECT 9, 23 UNION ALL
    	SELECT 6, 35 UNION ALL
    	SELECT 8, 22
    )
    SELECT W, X FROM A WHERE NOT EXISTS(SELECT * FROM B where W=Y AND X=Z)
    UNION ALL
    SELECT  Y, Z FROM B
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    W      X
    --------
    3	62
    6	17
    6	17
    8	22
    9	23
    6	35
    8	22

  5. #5
    Expert éminent
    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 817
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    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 817
    Billets dans le blog
    14
    Par défaut
    Bowen, apparemment, tu n'as pas compris l'explication d'Oishiiii.

    Citation Envoyé par Bowen
    La requête
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT FactureLigne_Qte20, FactureLigne_PxNetHT FROM FactureLigne
    me renvoie 316 lignes

    La requête
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT DISTINCT FactureLigne_Qte20, FactureLigne_PxNetHT FROM FactureLigne
    me renvoie 256 lignes
    Ceci montre que ta table FactureLigne contient 316 - 256 = 60 couples {FactureLigne_Qte20, FactureLigne_PxNetHT} en double.

    Comme l'opération UNION supprime les doublons, l'union de la table FactureLigne avec une table vide supprime les doublons de la table FactureLigne et revient donc à faire une requête SELECT DISTINCT sur la table FactureLigne toute seule. Il est donc normal que le résultat de la requête UNION ne te retourne que 256 lignes.

    Dans l'ordre, le SGBD exécute les deux sous-requêtes, soit une liste de 316 + 0 = 316 lignes, puis il supprime les 60 doublons de cette liste et tu n'as plus que 256 lignes.

    Faisons-le avec ton dernier exemple :
    FactureLigne : 4 enreg [ Qte20 | PxNetHT ]
    • 3 | 62
    • 6 | 17
    • 6 | 35
    • 6 | 17

    _ArcFactureLigne : 4 enreg [ Qte20 | PxNetHT ]
    • 8 | 22
    • 9 | 23
    • 6 | 35
    • 8 | 22


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    SELECT FactureLigne_Qte20, FactureLigne_PxNetHT FROM FactureLigne
    UNION 
    SELECT FactureLigne_Qte20, FactureLigne_PxNetHT FROM _ArcFactureLigne
    Le SGBD met dans une liste le résultat des deux sous-requêtes, soit la liste suivante :
    [ Qte20 | PxNetHT ]
    • 3 | 62
    • 6 | 17
    • 6 | 35
    • 6 | 17
    • 8 | 22
    • 9 | 23
    • 6 | 35
    • 8 | 22


    Ensuite il supprime les lignes de la liste en double :
    • 3 | 62
    • 6 | 17
    • 6 | 35
    • 6 | 17
    • 8 | 22
    • 9 | 23
    • 6 | 35
    • 8 | 22


    Résultat final :
    • 3 | 62
    • 6 | 17
    • 6 | 35
    • 8 | 22
    • 9 | 23

    Soit 5 lignes de résultat.
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole, en retraite... mais toujours Autoentrepreneur à l'occasion.
    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 !

  6. #6
    Membre émérite
    Homme Profil pro
    Inscrit en
    Mars 2002
    Messages
    899
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations forums :
    Inscription : Mars 2002
    Messages : 899
    Par défaut
    Si si, j'avais bien compris... que je n'utilisais pas la bonne méthode. Ce que je voulais, c'était ne supprimer que les doublons créés par l'intersection des deux fichiers.
    Chose que je croyais possible directement par un UNION.
    Bref, j'ai encore dit une bêtise...

    Je vous remercie en tous cas pour tous ces éclaircissements.

    Ma requête correcte (dans mon code, donc dans une configuration un peu moins "scolaire") comprends donc un UNION ALL maintenant.

    Merci, à vous, ça fait du bien de revenir sur les fondamentaux.

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

Discussions similaires

  1. compter enregistrement dans une requete sql avec UNION
    Par dbzzzde dans le forum VBA Access
    Réponses: 2
    Dernier message: 24/10/2007, 10h43
  2. Problème UNION SELECT ALL
    Par benjisan dans le forum Requêtes et SQL.
    Réponses: 3
    Dernier message: 22/06/2007, 12h03
  3. Requête SQL avec UNION, sum et GROUP BY
    Par Guitariff dans le forum Langage SQL
    Réponses: 6
    Dernier message: 03/12/2006, 13h48
  4. [SQL] Optimisation union , distinct
    Par raj dans le forum Oracle
    Réponses: 5
    Dernier message: 18/05/2006, 10h17
  5. pb the requête sql clause UNION
    Par new_wave dans le forum MS SQL Server
    Réponses: 1
    Dernier message: 07/11/2005, 13h38

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