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

Requêtes MySQL Discussion :

ORDER BY avec UNION ALL


Sujet :

Requêtes MySQL

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2014
    Messages
    266
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Avril 2014
    Messages : 266
    Par défaut ORDER BY avec UNION ALL
    Bonjour,

    Je cherche à ordonner les résultats d'une requête par date avec l'utilisation de UNION ALL, mais visiblement l'utilisation des deux dans la même requête pose des problèmes :

    Ce code fonctionne et indique bien les dates dans l'ordre chronologique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT CAST(date_execution AS CHAR) FROM `table` WHERE MONTH(date_execution)='02' ORDER BY date_execution
    2015-02-04 00:00:00
    2015-02-04 00:00:00
    2015-02-04 14:00:00
    2015-02-04 14:00:00
    2015-02-19 00:00:00
    ...

    En revanche ce code ne marche pas puisque les dates apparaissent dans le désordre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    SELECT 'DATE'
     
    UNION ALL
    (
    SELECT CAST(date_execution AS CHAR) FROM `table` WHERE MONTH(date_execution)='02' ORDER BY date_execution
    )
    DATE
    2015-02-28 00:00:00
    2015-02-19 00:00:00
    2015-02-04 00:00:00
    2015-02-26 12:00:00
    2015-02-04 14:00:00
    ...

    Je précise bien évidemment que date_execution est bien un champ de type datetime.

    Quelqu'un a t il une idée sur ce résultat étrange ?

  2. #2
    Modérateur
    Avatar de al1_24
    Homme Profil pro
    Retraité
    Inscrit en
    Mai 2002
    Messages
    9 136
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    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 136
    Par défaut
    Et en ôtant les parenthèses autour de la seconde partie de l'UNION ?
    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 éclairé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2014
    Messages
    266
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Avril 2014
    Messages : 266
    Par défaut
    Citation Envoyé par al1_24 Voir le message
    Et en ôtant les parenthèses autour de la seconde partie de l'UNION ?
    En enlevant les parenthèses j'ai en retour :

    #1054 - Unknown column 'date_execution' in 'order clause'

  4. #4
    Membre éclairé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2014
    Messages
    266
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Avril 2014
    Messages : 266
    Par défaut
    Je viens de trouver un début de réponse sans savoir quelle est la solution définitive ici :

    http://stackoverflow.com/questions/6732661/incorrect-usage-of-union-and-order-by

    Apparemment il faut faire attention lorsque l'on utilise ORDER BY avec une requête utilisant UNION ALL car le résultat peut être différent de celui attendu.
    Si l'on lit le post jusqu'à la fin , on comprend qu'il est possible d'obtenir le bon résultat si l'on combine ORDER BY et LIMIT.
    Par exemple ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    SELECT 'DATE'
     
    UNION ALL
    (
    SELECT CAST(date_execution AS CHAR) FROM `table` WHERE MONTH(date_execution)='02'  ORDER BY date_execution LIMIT 10000)
    résultat :
    DATE
    2015-02-04 00:00:00
    2015-02-04 00:00:00
    2015-02-04 14:00:00
    2015-02-04 14:00:00
    2015-02-19 00:00:00
    2015-02-26 12:00:00
    ...
    Ca marche.

    Maintenant comment indiquer une clause LIMIT qui soit aussi grande que le nombre lignes possibles ?

    Y a t il une solution plus générale ?

  5. #5
    Membre éclairé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2014
    Messages
    266
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Avril 2014
    Messages : 266
    Par défaut
    Bon après quelques essais je viens de trouver une solution qui marche je pense dans tous les cas. Le principe à retenir est d'encapsuler la requête utilisant ORDER BY dans une table dérivée nommée "tab" dans l'exemple ci dessous :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT 'DATE'
     
    UNION ALL
    (
    SELECT ligne FROM (SELECT CAST(date_execution AS CHAR) ligne FROM `table` WHERE MONTH(date_execution)='02'  ORDER BY date_execution ) tab
        )
    résultat :

    DATE
    2015-02-04 00:00:00
    2015-02-04 00:00:00
    2015-02-04 14:00:00
    2015-02-04 14:00:00
    2015-02-19 00:00:00
    2015-02-26 12:00:00
    2015-02-28 00:00:00
    Même si la requête s'allonge un peu ça n'est finalement pas si compliqué que cela. (si vous trouver un peu plus court je suis preneur quand même).

  6. #6
    Membre prolifique Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    Février 2011
    Messages
    6 918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : Février 2011
    Messages : 6 918
    Par défaut
    Salut xounet.

    Est-ce que tu t'exerces à la manipulation des requêtes en MySql ?

    Voici un exemple de l'usage du "order by" et du limit" sur une table "test".
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    --------------
    SET AUTOCOMMIT = 0
    --------------
     
    --------------
    START TRANSACTION
    --------------
     
    --------------
    DROP DATABASE IF EXISTS `base`
    --------------
     
    --------------
    CREATE DATABASE `base`
            DEFAULT CHARACTER SET `latin1`
            DEFAULT COLLATE       `latin1_general_ci`
    --------------
     
    --------------
    DROP TABLE IF EXISTS `test`
    --------------
     
    --------------
    CREATE TABLE `test`
    ( `id`       integer unsigned auto_increment NOT NULL primary key,
      `datexec`  datetime                        NOT NULL
    ) ENGINE=InnoDB
      DEFAULT CHARSET=`latin1` COLLATE=`latin1_general_ci`
      ROW_FORMAT=COMPRESSED
    --------------
     
    --------------
    INSERT INTO `test` (`datexec`) VALUES
      ('2015-07-04 13:15:00'),
      ('2015-02-22 08:33:00'),
      ('2015-05-25 14:03:00'),
      ('2015-08-12 15:25:00'),
      ('2015-02-14 07:49:00'),
      ('2015-05-01 11:17:00'),
      ('2015-03-08 19:32:00'),
      ('2015-02-11 13:11:00'),
      ('2015-12-15 12:00:00'),
      ('2015-05-12 14:00:00')
    --------------
     
    --------------
    select * from test
    --------------
     
    +----+---------------------+
    | id | datexec             |
    +----+---------------------+
    |  1 | 2015-07-04 13:15:00 |
    |  2 | 2015-02-22 08:33:00 |
    |  3 | 2015-05-25 14:03:00 |
    |  4 | 2015-08-12 15:25:00 |
    |  5 | 2015-02-14 07:49:00 |
    |  6 | 2015-05-01 11:17:00 |
    |  7 | 2015-03-08 19:32:00 |
    |  8 | 2015-02-11 13:11:00 |
    |  9 | 2015-12-15 12:00:00 |
    | 10 | 2015-05-12 14:00:00 |
    +----+---------------------+
    --------------
    select * from test where month(datexec) = 5
    union all
    select * from test where month(datexec) = 2
    --------------
     
    +----+---------------------+
    | id | datexec             |
    +----+---------------------+
    |  3 | 2015-05-25 14:03:00 |
    |  6 | 2015-05-01 11:17:00 |
    | 10 | 2015-05-12 14:00:00 |
    |  2 | 2015-02-22 08:33:00 |
    |  5 | 2015-02-14 07:49:00 |
    |  8 | 2015-02-11 13:11:00 |
    +----+---------------------+
    --------------
    select * from test where month(datexec) = 5
    union all
    select * from test where month(datexec) = 2
    order by datexec
    --------------
     
    +----+---------------------+
    | id | datexec             |
    +----+---------------------+
    |  8 | 2015-02-11 13:11:00 |
    |  5 | 2015-02-14 07:49:00 |
    |  2 | 2015-02-22 08:33:00 |
    |  6 | 2015-05-01 11:17:00 |
    | 10 | 2015-05-12 14:00:00 |
    |  3 | 2015-05-25 14:03:00 |
    +----+---------------------+
    --------------
    select * from test where month(datexec) = 5
    union all
    select * from test where month(datexec) = 2
    order by datexec
    limit 1,4
    --------------
     
    +----+---------------------+
    | id | datexec             |
    +----+---------------------+
    |  5 | 2015-02-14 07:49:00 |
    |  2 | 2015-02-22 08:33:00 |
    |  6 | 2015-05-01 11:17:00 |
    | 10 | 2015-05-12 14:00:00 |
    +----+---------------------+
    --------------
    COMMIT
    --------------
     
    --------------
    SET AUTOCOMMIT = 1
    --------------
     
     
    Appuyez sur une touche pour continuer...
    La première requête extrait les lignes sans les trier. Nous avons l'ordre de lecture de chaque sous-requête, puisque on demande à extraire d'abord le mois 5 puis ensuite le mois 2.

    La seconde requête reprend la première requête et on ajoute le tri. Le tri est fait sur la totalité de l'extraction, c'est-à-dire après l'extraction des lignes de toutes les sous-requêtes.

    La troisième requête reprend la seconde requête et on ajoute un "limit 1,4". On saute la première ligne, et on prend les quatre suivantes.

    Comme tu le constates, il n'y a aucune difficulté !

    Si tu mets un "order by" dans une sous-requête de l'union, il sera sans effet, car les lignes seront juste extraites dans l'ordre de la lecture sans être triées.
    Autrement dit, le "order by" tout comme le "limit" vont s'appliquer sur le résultat final de l'extraction de l'union.

    En espérant avoir répondu à ta question.

    @+

  7. #7
    Membre éclairé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2014
    Messages
    266
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Avril 2014
    Messages : 266
    Par défaut
    Bonjour Artemus24,

    Merci d'avoir complété le sujet avec ces remarques concernant le tri de deux requêtes groupées. La difficulté dans mon cas de figure peu fréquent je pense est que l'union porte sur deux requêtes qui n'ont rien en commun, ni la table, ni les champs : la première retourne simplement un champ "texte", la seconde affiche une colonne de dates (SELECT "date" n'est pas là pour le décor c'est réellement ce type de requête que je fais...), donc order by ne peut rien faire du tout d'où la solution de la sous requête...

  8. #8
    Modérateur

    Profil pro
    dba
    Inscrit en
    Janvier 2010
    Messages
    5 643
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : dba

    Informations forums :
    Inscription : Janvier 2010
    Messages : 5 643
    Par défaut
    Bonjour,

    En enlevant les parenthèses j'ai en retour :

    #1054 - Unknown column 'date_execution' in 'order clause'
    C'est dû au fait qu'avec UNION, le nom des colonne est celui de la première requete.
    Mais même si vous ajoutez un alias poru la première requete, la ligne "DATE" va a priori se retrouver à la fin.


    Je doute qu'aucune de ces solutions ne garantisse l'ordre.
    Un ORDER BY dans une sous requete n'a aucun sens.

    Ceci devrait fonctionner :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    SELECT 1 as Ordre, 'DATE' as date_execution
    UNION ALL
    SELECT 2, CAST(date_execution AS CHAR) FROM `table` WHERE MONTH(date_execution)='02' 
    ORDER BY Ordre, date_execution

  9. #9
    Membre éclairé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2014
    Messages
    266
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Avril 2014
    Messages : 266
    Par défaut
    Oui tout à fait ton exemple marche aussi et illustre bien le fonctionnement de ORDER BY avec UNION ALL c'est très explicite comme solution.

    En fait le résultat que souhaite obtenir nécessite une petite modification comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    SELECT date_execution FROM ( 
    SELECT 1 as ordre,'DATE' as date_execution 
    UNION ALL 
    SELECT 2 as ordre, CAST(date_execution AS CHAR) FROM `table` WHERE MONTH(date_execution)='02' ORDER BY ordre,date_execution) T
    "L'encapsulation" comme décrit plus haut marche très bien aussi et évite de spécifier une variable ordre, puisque l'ordre est celui de la requête.

  10. #10
    Modérateur

    Profil pro
    dba
    Inscrit en
    Janvier 2010
    Messages
    5 643
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : dba

    Informations forums :
    Inscription : Janvier 2010
    Messages : 5 643
    Par défaut
    Citation Envoyé par xounet Voir le message
    En fait le résultat que souhaite obtenir nécessite une petite modification comme ceci :

    Non, la modification correcte serait plutôt celle-ci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    SELECT date_execution 
    FROM ( 
        SELECT 1 as ordre,'DATE' as date_execution 
        UNION ALL 
        SELECT 2 as ordre, CAST(date_execution AS CHAR) FROM `table` WHERE MONTH(date_execution)='02' 
    ) T
    ORDER BY ordre,date_execution
    Car comme je le disais, un ORDER BY dans une sous requete n'a pas de sens, car le résultat d'une sous requete est une pseudo-table, donc sans notion d'ordre. (une exception toutefois, lors de l'utilisation conjointe avec une clause [c]LIMIT/], car alors c'est le contenu de la pseudo-table qui est différent)

    En fait, l'optimiseur devrait même ne pas tenir compte du ORDER BY dans la sous requete.

    En fait il semble qu'il en tienne compte et que vous obtenez du coup l'ordre désiré, mais je doute que cela soit garanti en toutes circonstances.

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

Discussions similaires

  1. Optimisation d'une requête avec UNION ALL
    Par jgfa9 dans le forum Requêtes
    Réponses: 11
    Dernier message: 01/08/2012, 21h53
  2. Illegal mix of collations avec UNION ALL
    Par lodan dans le forum Outils
    Réponses: 1
    Dernier message: 03/05/2007, 18h05
  3. order by avec un union ALL
    Par roxxxy dans le forum Langage SQL
    Réponses: 1
    Dernier message: 20/03/2007, 15h59
  4. Réponses: 10
    Dernier message: 30/11/2004, 10h12
  5. Problème requête avec UNION et ORDER BY
    Par Yann21 dans le forum Langage SQL
    Réponses: 12
    Dernier message: 12/12/2003, 11h02

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