Erreur Postgres [la colonne doit apparaître dans la clause GROUP BY ou être utilisée dans une fonction d'agrégation] lorsque la sous-requête est utilisée


16

J'ai deux tables employeeet phones. Un employé peut avoir de 0 à n numéros de téléphone. Je veux lister les noms des employés avec leurs numéros de téléphone. J'utilise la requête ci-dessous qui fonctionne bien.

SELECT empname,array_agg(phonenumber) AS phonenumbers 
FROM employee LEFT OUTER JOIN phones ON employee.empid = phones.empid
GROUP BY employee.empid

entrez la description de l'image ici

La table des employés peut contenir un grand nombre de lignes. Je souhaite récupérer uniquement certains employés à la fois. Par exemple, je veux récupérer 3 employés avec leurs numéros de téléphone. J'essaie d'exécuter cette requête.

SELECT empname,array_agg(phonenumber) AS phonenumbers 
FROM 
(SELECT * FROM employee ORDER BY empname LIMIT 3 OFFSET 0) AS employee 
LEFT OUTER JOIN phones ON employee.empid = phones.empid
GROUP BY employee.empid

Mais je reçois cette erreur. ERROR: column "employee.empname" must appear in the GROUP BY clause or be used in an aggregate function La seule différence entre deux requêtes est que j'utilise une sous-requête dans cette dernière pour limiter les lignes avant de les rejoindre. Comment résoudre cette erreur?

Réponses:


21

La fonctionnalité de Postgres de pouvoir utiliser la clé primaire d'une table avec GROUP BYet pas besoin d'ajouter les autres colonnes de cette table dans la GROUP BYclause est relativement nouvelle et ne fonctionne que pour les tables de base. L'optimiseur n'est pas (encore?) Assez intelligent pour identifier les clés primaires des vues, des ctes ou des tables dérivées (comme dans votre cas).

Vous pouvez ajouter les colonnes de votre choix SELECTdans la GROUP BYclause:

SELECT e.empname, array_agg(p.phonenumber) AS phonenumbers 
FROM 
(SELECT * FROM employee ORDER BY empname LIMIT 3 OFFSET 0) AS e 
LEFT OUTER JOIN phones AS p ON e.empid = p.empid
GROUP BY e.empid, e.empname 
ORDER BY e.empname ;

ou utilisez une sous-requête (et transférez-la GROUP BY):

SELECT e.empname,
       (SELECT array_agg(p.phonenumber) 
        FROM phones AS p
        WHERE e.empid = p.empid
       ) AS phonenumbers 
FROM 
(SELECT * FROM employee ORDER BY empname LIMIT 3 OFFSET 0) AS e 
ORDER BY e.empname ;

qui pourrait également s'écrire:

SELECT e.empname,
       (SELECT array_agg(p.phonenumber) 
        FROM phones AS p
        WHERE e.empid = p.empid
       ) AS phonenumbers 
FROM employee AS e
ORDER BY e.empname LIMIT 3 OFFSET 0 ;

Puisque vous êtes dans la version 9.3+. vous pouvez également utiliser une LATERALjointure:

SELECT e.empname,
       p.phonenumbers 
FROM 
   (SELECT * FROM employee ORDER BY empname LIMIT 3 OFFSET 0) AS e
LEFT JOIN LATERAL
   (SELECT array_agg(phonenumber) AS phonenumbers
    FROM phones 
    WHERE e.empid = phones.empid
   ) AS p ON TRUE 
ORDER BY e.empname ;

@ypercude Merci. C'est une manière simple et propre.
Programmeur
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.