web-dev-qa-db-fra.com

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

J'ai deux tables employee et 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

enter image description here

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 rejoindre. Comment résoudre cette erreur?

18
Programmer

La fonctionnalité de Postgres pour pouvoir utiliser la clé primaire d'une table avec GROUP BY Et ne pas avoir besoin d'ajouter les autres colonnes de cette table dans la clause GROUP BY Est relativement nouvelle et ne fonctionne que pour la base les tables. 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 souhaitées dans la SELECT dans la clause GROUP BY:

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-y le 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 jointure LATERAL:

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 ;
22
ypercubeᵀᴹ