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 PostgreSQL Discussion :

Ajouter une donnée selon un compteur


Sujet :

Requêtes PostgreSQL

  1. #1
    Membre régulier
    Ajouter une donnée selon un compteur
    Bonjour,
    Je fais une requête SELECT avec une jointure et un group by pour compter le nombre d'éléments associés, ce qui ressemble à ça :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    select table1.id, table1.name, count(table2.id) as c from table1 left join table2 on table2.link1=table1.id group by table1.id order by table1.name

    J'ai le bon résultat, avec des tableaux contenant [id, name, c].
    Ce qui m'embête c'est qu'en PHP derrière je dois boucler sur tous les résultats pour ajouter une donnée me facilitant l'affichage (une classe CSS), selon si la valeur de 'c' est supérieur à 0 ou non
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    foreach ($data as &$s) {
    	$s['class'] = $s['c'] < 1 ? 'txt-gray' : '';
    }

    Y a-t-il moyen d'ajouter cette donnée directement via PostgreSQL ? Par exemple en ajoutant après 'c' une donnée 'class' avec un test 'if' sur la valeur de 'c' ?

    Merci

  2. #2
    Membre expérimenté
    Bonjour,

    si vous attendez qu'on vous aide, merci de faire un tant soit peu de présentation du code.
    le langage SQL n'est sensible ni aux retours lignes, ni aux tabulations, ni aux espaces surnuméraires.

    Une fois présenté votre code ressemble à ceci :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    select table1.id
        , table1.name
        , count(table2.id) as c 
    from table1 
        left join table2 
            on table2.link1=table1.id 
    group by table1.id 
    order by table1.name

    Et là c'est évident, la syntaxe est incorrecte aux alentours du Group By

    Ça c'est dit, passons à la demande.

    Que représente 'txt-gray' ?
    Pour moi c'est un assemblage de 2 colonnes : [object] + '-' + [color]
    D'autre part, quand la valeur est > 0 alors le résultat est : "" (chaine vide)
    Que valent chacune des colonnes ?

    Je te rassure on peut tout faire en SQL.
    Pour autant la bonne utilisation du langage est l’utilisation d'une information vectorisée et atomique.

    Si, on pousse ta demande, et en ne prenant pas attention à ce qui vient d'être dit, alors on peut imaginer que la requête SQL délivre directement un flux HTML.

    Pour faire ça tu auras besoin de
    Union All
    Case ... when ... then ...else ...end

    Mais est-ce au SQL de faire ça ?
    Comment va se passer les mises à jour si demain on veut aussi griser le fond et le mettre le texte en Italique ?
    Le savoir est une nourriture qui exige des efforts.

  3. #3
    Membre régulier
    Bonsoir,
    Je ne vois pas le problème aux alentours du group by, ma requête fonctionne d'après mon test à l'instant.
    Désolé si la mise en couleur ne suffit pas à rendre suffisamment lisible le code SQL.

    'class' est un nom de classe CSS, ne correspondant pas à des colonnes, elle est soit vide, soit la chaine 'txt-gray', selon le compteur 'c'.
    J'ai besoin de l'ajouter aux résultats avant de l'envoyer à la couche Vue de mon système MVC. Je me demandais si ce serait pratique que SQL me le fasse puisque c'est déjà lui qui calcule le compteur, au lieu d'avoir sans cesse des foreach sur mes tableaux de résultat.

    Je crée mes requêtes avec une sorte d'ORM et des constantes ou variables PHP qui font que je change la valeur de la chaine de classe sans problème, ce n'est jamais en dur dans la code SQL.

    Case ... when ... then ...else ...end semble bien s'appliquer à ma question.
    Le code suivant donne le résultat que j'attends :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    select table1.id
        , table1.name
        , count(table2.id) as c 
        , case
            when count(table2.id) = 0
            then 'txt-gray'
            else ''
         end
         as class 
    from table1 
        left join table2 
            on table2.link1=table1.id 
    group by table1.id 
    order by table1.name

    Merci pour l'astuce !

  4. #4
    Membre averti
    Bonjour bonjour,

    Par défaut, un group by qui n'a pas toutes les expressions du select, ca fait mal au cœur.

    Pour votre demande, le sql n'est pas prévu pour faire de l'affichage tout beau tout propre mais pour renvoyer des données d'où le fait que la demande soit pas folichonne.

    Si toutefois vous voulez absolument le faire, quitte à y être, autant encapsuler votre requête dans une CTE (la petite clause WITH), faire un select cte.* et rajouter, dans le select, un case du style
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    Case WHEN macolone = madonne THEN null ELSE 'txt-gray' END CSS


    Et voili voilou, comme dirait l'autre.

    Mais ne déportez pas au sql ce que du php ou autre peut faire...

    Bisous bisous

  5. #5
    Membre régulier
    Merci @JeanYvette.
    C'est bien la proposition de Michel.Priori avec CASE qui est dédié à mon cas comme vous le proposez aussi.

    Par contre je ne vois pas l'intérêt de mettre toutes les données du select dans le groupby. Puisqu'en regroupant sur 1 colonne (l'identifiant) ça fait le boulot, pourquoi ajouter des contraintes ou alourdir le groupby alors que ça ne changera pas le résultat ? Peut-être certains cas (jointures plus complexes) peuvent le nécessiter mais je ne comprends pas l'intérêt dans mon exemple. Merci de m'éclairer.

    Et attention à la présentation du code

  6. #6
    Membre expérimenté
    Bonjour,


    Citation Envoyé par Titum Voir le message
    Je ne vois pas le problème aux alentours du group by, ma requête fonctionne d'après mon test à l'instant.
    Ah les tests !
    Et que donne celui-ci ?
    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
    create table test1 
      (id int
      , name varchar(10))
    ;
     
    insert into test1 values
       (1,'Nom impair')
      ,(2,'Nom pair')
      ,(3,'Nom impair')
    ;
     
    select id, name , count(*) as nb
      from test1
      group by id
     ;


    Quelle est votre version de PostgreSQL ?
    Le savoir est une nourriture qui exige des efforts.

  7. #7
    Membre régulier
    Ca provoque une erreur dans votre exemple.
    Mais c'est parce qu'il manque une information, la colonne "id" est une clé primaire. Je crée toujours la colonne d'identifiant sous forme de clé primaire, j'ai omis de le mentionner, ma question ne portais pas sur la structure de ma table.
    Y a-t-il donc plus de logique avec cette info, à ne grouper que sur "id", ou bien je manque encore quelque chose ?

    J'utilise postgreSQL 10.1

  8. #8
    Membre expérimenté
    Bonjour,

    Il suffit de pousser les tests et ajouter la contrainte PK pour affiner la compréhension ... compréhension que vous pouvez partager.
    C'est l'objet du forum = l'entraide.

    Entraide :
    principe 1 : "aide toi et le ciel t'aidera"
    principe 2 : "l'exercice du doute permet d'affiner la compréhension"
    principe 3 : "le savoir augmente quand on le partage"
    Le savoir est une nourriture qui exige des efforts.

  9. #9
    Modérateur

    Bonjour,

    Citation Envoyé par Michel.Priori Voir le message

    Et là c'est évident, la syntaxe est incorrecte aux alentours du Group By
    Citation Envoyé par JeanYvette Voir le message
    Par défaut, un group by qui n'a pas toutes les expressions du select, ca fait mal au cœur.
    La syntaxe du GROUP BY me semble correcte.

    en effet, Titum n'a pas posté la structure de la table en question, mais on peut supposer que la colonne "id" est la clef primaire.
    Dans ce cas, la colonne "nom" dépend fonctionnellement de la colonne "id" et n'a donc pas besoin d'être explicitement précisée dans le GROUP BY.

  10. #10
    Expert éminent
    Bonjour,
    Je ne sais pas ce qu'il en est de PostgreSQL, mais SqlServer n'est pas d'accord:
    Code sql :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    select cd_c_code,cd_c_nom, count(*) 
    from CLIENT_DETAIL
    group by CD_C_CODE

    cd_c_code est la clé primaire de la table.
    La réponse est sans appel:

    Msg*8120, Niveau*16, État*1, Ligne*1
    La colonne "CLIENT_DETAIL.CD_C_NOM' n'est pas valide dans la liste de sélection parce qu'elle n'est pas contenue dans une fonction d'agrégation ou dans la clause GROUP BY.
    Tatayo.

  11. #11
    Modérateur

    Citation Envoyé par Michel.Priori Voir le message

    Y a-t-il donc plus de logique avec cette info, à ne grouper que sur "id", ou bien je manque encore quelque chose ?
    Non, c'est bien ça.

    d'après la norme SQL, dans une requete avec regroupement, toute colonne du SELECT doit soit :
    1/ être inclues dans une fonction d'agregation (SUM, MAX,...)
    2/ soit faire partie du regroupement ou dépendre fonctionnellement de celui-ci


    La partie en gras (apparue il me semble dans la norme de 1999) permet effectivement d’omettre la colonne "name" dans cet exemple, à condition que la clef soit effectivement déclarée afin d'assurer la dépendance fonctionnelle.

    A ma connaissance, Postgre est le seul SGBDR a implémenter -correctement (mysql si tu me lis)- cette subtilité.

    Si dans le même exemple (avec la clef primaire sur id déclarée) on fait la même requête mais en regroupant sur la colonne name uniquement, alors on aura une erreur (contrairement à mysql qui renverra... "quelque chose" )

  12. #12
    Modérateur

    Citation Envoyé par tatayo Voir le message

    Je ne sais pas ce qu'il en est de PostgreSQL, mais SqlServer n'est pas d'accord:
    Oracle non plus, mais avec un message bien plus laconique :

    not a GROUP BY expression

  13. #13
    Membre expérimenté
    Bonjour,

    Citation Envoyé par aieeeuuuuu Voir le message
    2/ soit faire partie du regroupement ou dépendre fonctionnellement de celui-ci
    [...]
    A ma connaissance, Postgre est le seul SGBDR a implémenter -correctement (mysql si tu me lis)- cette subtilité.
    Et bien, merci, car moi non plus je n'avais pas connaissance de cette subtilité, qui me semble :
    - légitime du point de vue théorique
    - compliquée à mettre en œuvre lors des jointures multiples (surtout pour la relecture de rq longues)

    J'ai du aller sur le site https://dbfiddle.uk/?rdbms=postgres_10 pour valider le test ; c'est pas que je n'avais pas confiance...
    Le savoir est une nourriture qui exige des efforts.

  14. #14
    Rédacteur

    Citation Envoyé par aieeeuuuuu Voir le message
    d'après la norme SQL, dans une requete avec regroupement, toute colonne du SELECT doit soit :
    1/ être inclues dans une fonction d'agregation (SUM, MAX,...)
    2/ soit faire partie du regroupement ou dépendre fonctionnellement de celui-ci
    Tout à fait....


    La partie en gras (apparue il me semble dans la norme de 1999) permet effectivement d’omettre la colonne "name" dans cet exemple, à condition que la clef soit effectivement déclarée afin d'assurer la dépendance fonctionnelle.
    Je confirme

    A ma connaissance, Postgre est le seul SGBDR a implémenter
    Mais que se passera t-il si la clef évolue ? N'oubliez pas que ce qui fait la force des SGBDR est la facilité d'évolution des structures de données....
    1) la colonne n'est plus la clef primaire => résultat différent potentiellement faux
    2) la clef primaire est augmentée d'une colonne supplémentaire => résultat différent potentiellement faux
    3) quid de la rétro compatibilité avec une version antérieure => résultat différent potentiellement faux

    C'est pour toutes ces raisons et aussi pour une meilleure optimisation que les grand SGBDR ont opté pour un SQL strict et non ce dangereux raccourci !

    A +
    Cette signature n'a pas pu être affichée car elle comporte des erreurs.

  15. #15
    Modérateur

    Citation Envoyé par SQLpro Voir le message

    Mais que se passera t-il si la clef évolue ? N'oubliez pas que ce qui fait la force des SGBDR est la facilité d'évolution des structures de données....
    1) la colonne n'est plus la clef primaire => résultat différent potentiellement faux
    2) la clef primaire est augmentée d'une colonne supplémentaire => résultat différent potentiellement faux
    3) quid de la rétro compatibilité avec une version antérieure => résultat différent potentiellement faux
    Non, dans ces cas, on aura une erreur :
    Pour les 1/ et 2/, car postgre, à la différence de mysql, vérifie la dépendance fonctionnelle. si la clef change, alors la dépendance fonctionnelle n'est plus vraie, et on aura une erreur.
    pour le cas 3/ pour les anciennes versions, on aura aussi une erreur, pour les mêmes raison que sur les autres SGBDR.

  16. #16
    Modérateur

    J'aurai opté pour une de ces deux syntaxes de mon côté :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
      select t1.id
           , t1.name
           , coalesce((select count(t2.id) from table2 as t2 where t2.link1 = t1.id), 0) as c
        from table1 as t1
    order by t1.name;
     
        select t1.id
             , t1.name
             , coalesce(t2.c, 0) as c
          from table1 as t1
     left join (select link1, count(*) as c from table2 group by link1) as t2 on t2.link1 = t1.id
      order by t1.name;


    Merci aieeeuuuuu pour la règle sur la dépendance fonctionnelle, j'ignorais que PostGre l'avait implémentée (et correctement de surcroît !).

  17. #17
    Membre régulier
    hhmmmm
    J'ai toujours eu une colonne PK dans mes tables sur laquelle j'ai fait les group by sans ajouter la liste (potentiellement longue) des autres colonnes récupérées.
    Et avec MySQL et Postgre je n'ai jamais eu de problème, et je crois bien même avec Firebird il y a quelques années.

    La raison principale je crois c'est qu'en général je groupe sur l'ID de la table principale et que je veux faire un simple comptage sur une table jointe. C'est un cas simple.

    Maintenant en lisant vos commentaires, je conçois que cela peut poser des problèmes. C'est éclairant, merci à tous.

###raw>template_hook.ano_emploi###