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

PostgreSQL Discussion :

Petite interrogation....SELECT ... GROUP BY


Sujet :

PostgreSQL

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    489
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2004
    Messages : 489
    Points : 388
    Points
    388
    Par défaut Petite interrogation....SELECT ... GROUP BY
    Bonjour,

    J'aimerais bien avoir quelques explications sur ce genre d'erreurs ..
    Par exemple, voici une requete :

    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
    $requete = "
    SELECT 
    vendeurs.nom, 
    vendeurs.id_vendeur, 
    dossiers.id_dossier, 
    COUNT(id_dossier_vendeur.id_dossier) AS nb_dossier,
    SUM(dossiers.total) AS ca_total
    FROM
    ".$GLOBALS['prefixe'].".vendeurs,
    ".$GLOBALS['prefixe'].".id_dossier_vendeur,
    ".$GLOBALS['prefixe'].".dossiers
    WHERE
    vendeurs.id_vendeur = id_dossier_vendeur.id_vendeur
    AND dossiers.id_dossier = id_dossier_vendeur.id_dossier
    GROUP BY 
    vendeurs.nom, 
    vendeurs.id_vendeur
    ";

    Si je tente cette requete, voici la reponse de postgres :
    Warning: pg_query() [function.pg-query]: Query failed: ERROR: column "dossiers.id_dossier" must appear in the GROUP BY clause or be used in an aggregate function in /usr/local/apache/htdocs/scripts/V2/stats_vendeurs.php on line 109
    ERROR: column "dossiers.id_dossier" must appear in the GROUP BY clause or be used in an aggregate function SELECT vendeurs.nom, vendeurs.id_vendeur, dossiers.id_dossier, COUNT(id_dossier_vendeur.id_dossier) AS nb_dossier, SUM(dossiers.total) AS ca_total FROM clients_dossiers.vendeurs, clients_dossiers.id_dossier_vendeur, clients_dossiers.dossiers WHERE vendeurs.id_vendeur = id_dossier_vendeur.id_vendeur AND dossiers.id_dossier = id_dossier_vendeur.id_dossier GROUP BY vendeurs.nom, vendeurs.id_vendeur

    Par contre, si je supprime dossiers.id_dossier de mon select, ca passe impeccable..
    Mais je ne vois pas pourquoi je suis obligé de faire passer dossiers.id_dossier dans le group by..

    Postgres semble tres chatouilleux sur ce point la.. j'ai lu la doc sur le sujet, mais ca n'est pas tres clair..

    La clause Clause GROUP BY est utilisée pour regrouper des lignes d'une table partageant les mêmes valeurs dans toutes les colonnes précisées. L'ordre dans lequel ces colonnes sont indiquées importe peu. L'effet est de combiner chaque ensemble de lignes partageant des valeurs communes en un seul groupe de ligne représentant toutes les lignes du groupe. Ceci se fait en éliminant les redondances dans la sortie et/ou pour calculer les agrégats s'appliquant à ces groupes. Par exemple :

    => SELECT * FROM test1;
    x | y
    ---+---
    a | 3
    c | 2
    b | 5
    a | 1
    (4 rows)

    => SELECT x FROM test1 GROUP BY x;
    x
    ---
    a
    b
    c
    (3 rows)

    Dans la seconde requête, nous n'aurions pas pu écrire SELECT * FROM test1 GROUP BY x parce qu'il n'existe pas une seule valeur pour la colonne y pouvant être associé avec chaque autre groupe. Les colonnes de regroupement peuvent être référencées dans la liste de sélection car elles ont une valeur constante unique par groupe.

    En général, si une table est groupée, les colonnes qui ne sont pas utilisées dans le regroupement ne peuvent pas être référencées sauf dans les expressions d'agrégats. Voici un exemple d'expressions d'agrégat :

    => SELECT x, sum(y) FROM test1 GROUP BY x;
    x | sum
    ---+-----
    a | 4
    b | 5
    c | 2
    (3 rows)

    Ici, sum est la fonction d'agrégat qui calcule une seule valeur pour le groupe entier. Plus d'informations sur les fonctions d'agrégats disponibles sont proposées dans Section 9.15.

    Astuce : Le regroupement sans expressions d'agrégats calcule effectivement l'ensemble les valeurs distinctes d'une colonne. Ceci peut aussi se faire en utilisant la clause DISTINCT (voir Section 7.3.3).

    Voici un autre exemple : il calcule les ventes totales pour chaque produit (plutôt que le total des ventes sur tous les produits).

    SELECT produit_id, p.nom, (sum(v.unite) * p.prix) AS ventes
    FROM produits p LEFT JOIN ventes v USING (produit_id)
    GROUP BY produit_id, p.nom, p.prix;

    Dans cet exemple, les colonnes produit_id, p.nom et p.prix doivent être dans la clause GROUP BY car elles sont référencées dans la liste de sélection de la requête. (Suivant la façon dont est conçue la table produits, le nom et le prix pourraient être totalement dépendants de l'ID du produit, donc des regroupements supplémentaires pourraient théoriquement être inutiles mais ceci n'est pas encore implémenté.) La colonne s.unite n'a pas besoin d'être dans la liste GROUP BY car elle est seulement utilisée dans l'expression de l'agrégat (sum(...)) représentant les ventes d'un produit. Pour chaque produit, la requête renvoie une ligne de résumé sur les ventes de ce produit.

    En SQL strict, GROUP BY peut seulement grouper les colonnes de la table source mais PostgreSQL étend ceci en autorisant GROUP BY à grouper aussi les colonnes de la liste de sélection. Grouper par expressions de valeurs au lieu de simples noms de colonnes est aussi permis.

    Si une table a été groupée en utilisant la clause GROUP BY mais que seul certains groupes sont intéressants, la clause HAVING peut être utilisée, plus comme une clause WHERE, pour éliminer les groupes d'une table groupée. Voici la syntaxe :

    SELECT liste_selection FROM ... [WHERE
    ...] GROUP BY ... HAVING
    expression_booléenne

    Les expressions de la clause HAVING peuvent référer à la fois aux expressions groupées et aux expressions non groupées (ce qui impliquent nécessairement une fonction d'agrégat).

    Exemple :

    => SELECT x, sum(y) FROM test1 GROUP BY x HAVING sum(y) > 3;
    x | sum
    ---+-----
    a | 4
    b | 5
    (2 rows)

    => SELECT x, sum(y) FROM test1 GROUP BY x HAVING x < 'c';
    x | sum
    ---+-----
    a | 4
    b | 5
    (2 rows)

    De nouveau, un exemple plus réaliste :

    SELECT produit_id, p.nom, (sum(v.unite) * (p.prix - p.cout)) AS profit
    FROM produits p LEFT JOIN ventes v USING (produit_id)
    WHERE v.date > CURRENT_DATE - INTERVAL '4 weeks'
    GROUP BY produit_id, p.nom, p.prix, p.cout
    HAVING sum(p.prix * s.unite) > 5000;

    Dans l'exemple ci-dessus, la clause WHERE sélectionne les lignes par une colonne qui n'est pas groupée (l'expression est vraie seulement pour les ventes des quatre dernières semaines) alors que la clause HAVING restreint la sortie aux groupes dont le total des ventes dépasse 5000. Notez que les expressions d'agrégats n'ont pas besoin d'être identiques dans toutes les parties d'une requête.

    Et si j'essaye sans "Group by" .. dans l'espoir de recuperer tous les id_dossiers, il me sort aussi une erreur..
    Warning: pg_query() [function.pg-query]: Query failed: ERROR: column "vendeurs.nom" must appear in the GROUP BY clause or be used in an aggregate function in /usr/local/apache/htdocs/scripts/V2/stats_vendeurs.php on line 106
    ERROR: column "vendeurs.nom" must appear in the GROUP BY clause or be used in an aggregate function SELECT vendeurs.nom, vendeurs.id_vendeur, dossiers.id_dossier, COUNT(id_dossier_vendeur.id_dossier) AS nb_dossier, SUM(dossiers.total) AS ca_total FROM clients_dossiers.vendeurs, clients_dossiers.id_dossier_vendeur, clients_dossiers.dossiers WHERE vendeurs.id_vendeur = id_dossier_vendeur.id_vendeur AND dossiers.id_dossier = id_dossier_vendeur.id_dossier

    Bref... c'est le COUNT qui oblige a tout grouper ? ou comment ca marche ..? j'arrive a passer outre ces limitations, mais j'aime bien comprendre les motifs ..

  2. #2
    Expert éminent
    Avatar de GrandFather
    Inscrit en
    Mai 2004
    Messages
    4 587
    Détails du profil
    Informations personnelles :
    Âge : 54

    Informations forums :
    Inscription : Mai 2004
    Messages : 4 587
    Points : 7 103
    Points
    7 103
    Par défaut
    Bonjour,

    en fait, c'est logique : s'il y a plusieurs id_dossier possibles pour un même vendeur, il est obligatoire de les regrouper pour rendre le calcul de COUNT() et SUM() possibles. C'est pour cela que Postgres t'oblige à les déclarer dans le GROUP BY.
    FAQ XML
    ------------
    « Le moyen le plus sûr de cacher aux autres les limites de son savoir est de ne jamais les dépasser »
    Giacomo Leopardi

  3. #3
    Membre averti

    Homme Profil pro
    Inscrit en
    Janvier 2005
    Messages
    338
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 338
    Points : 404
    Points
    404
    Par défaut
    Bonjour

    tu aurais eu la même erreur sous Oracle.
    Christophe Chauvet
    Consultant Odoo
    Python / PostgreSQL

  4. #4
    Membre à l'essai
    Profil pro
    Inscrit en
    Janvier 2012
    Messages
    22
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2012
    Messages : 22
    Points : 19
    Points
    19
    Par défaut
    Avez-vous trouvé une solution parce que j'ai le même problème et je n'arrive pas à le résoudre...

  5. #5
    Rédacteur

    Avatar de ok.Idriss
    Homme Profil pro
    IS Consultant
    Inscrit en
    Février 2009
    Messages
    5 220
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Février 2009
    Messages : 5 220
    Points : 19 452
    Points
    19 452
    Par défaut
    Bonjour.

    Toute colonne présente dans le SELECT doit l'être dans la clause GROUP BY ou bien dans une fonction d'agrégation, c'est la norme SQL qui l'impose. Si tu as plus l'habitude de MySQL, elle est certes plus laxiste à ce sujet mais c'est pas une raison pour faire sale.

    Donc oui, l'erreur sous Postgres est tout à fait normale ... qu'est-ce qui t'empêche d'ajouter la colonne impactée à la fin de ta clause (en dernier) ? Ça résout pas ton problème ?

    Cordialement,
    Idriss

  6. #6
    Membre à l'essai
    Profil pro
    Inscrit en
    Janvier 2012
    Messages
    22
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2012
    Messages : 22
    Points : 19
    Points
    19
    Par défaut
    Ma requête est la suivante:

    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
    SELECT coo_trk_id, coo_time, coo_length, MIN(distance) AS distance
    FROM (
    SELECT coo_trk_id, coo_time, coo_length, 
      (SELECT ST_Distance(
      geography(st_transform(coo_geom::geometry,4326)),
      ST_GeographyFromText('POINT(3.075206 50.369314)')    
      )) AS distance
        FROM coordinates, track
        WHERE coo_trk_id=trk_id AND trk_status IN (0,4) AND trk_veh_id=1
        AND ST_Y(coo_geom::geometry) BETWEEN 50.1891338198 AND 50.5494941802
        AND ST_X(coo_geom::geometry) BETWEEN 2.79271955988 AND 3.35769244012
       ORDER BY distance
      ) AS alldistances
    GROUP BY coo_trk_id
    ORDER BY distance;

    J'ai bien compris que ce qui est dans le select doit être dans le group by ou une fonction d'agrégation. Vous me dites de rajouter à ma clause group by coo_time et coo_length mais je n'obtiens pas le même résultat... Je ne vois pas comment faire.

    Merci

  7. #7
    Rédacteur

    Avatar de ok.Idriss
    Homme Profil pro
    IS Consultant
    Inscrit en
    Février 2009
    Messages
    5 220
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Février 2009
    Messages : 5 220
    Points : 19 452
    Points
    19 452
    Par défaut
    mais je n'obtiens pas le même résultat...
    Le même résultat par rapport à quoi ? Quel requête ? Si ce n'est pas le résultat attendu, c'est que la requête n'est pas bonne ... et avec aussi peu de détails sur les tables, on va pas pouvoir deviner.

    A ce stade il vaut mieux ouvrir une nouvelle discussion en détaillant le schéma de tes tables, des exemples de tuples, le résultat obtenu avec ta requête et le résultat attendu et les écarts constatés. Bref, plus de détails ... y-aura pas de solution de passe passe qui va rendre une requête incorrecte en requête correcte, surtout pour une requête aussi spécifique.

    Je passe en sinon.

    Idriss

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

Discussions similaires

  1. Select / Group by / Having
    Par jnauche dans le forum SQL
    Réponses: 3
    Dernier message: 14/05/2008, 11h09
  2. [Conception] Petites interrogations pour un portail évènementiel
    Par Him dans le forum PHP & Base de données
    Réponses: 4
    Dernier message: 15/12/2007, 13h32
  3. Petit problème SQL (GROUP BY|ORDER BY)
    Par kalash_jako dans le forum Langage SQL
    Réponses: 4
    Dernier message: 09/04/2007, 23h17
  4. Petite interrogation sur les z-index et div
    Par Delphy113 dans le forum Balisage (X)HTML et validation W3C
    Réponses: 2
    Dernier message: 13/02/2006, 21h09

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