Erro do Postgres [a coluna deve aparecer na cláusula GROUP BY ou ser usada em uma função agregada] quando a subconsulta é usada

16

Eu tenho duas mesas employeee phones. Um funcionário pode ter de 0 a n números de telefone. Quero listar os nomes dos funcionários com seus números de telefone. Estou usando a consulta abaixo, que funciona bem.

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

insira a descrição da imagem aqui

A tabela de funcionários pode conter um grande número de linhas. Quero buscar apenas alguns funcionários de cada vez. Por exemplo, quero buscar três funcionários com seus números de telefone. Estou tentando executar esta consulta.

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

Mas eu recebo esse erro. ERROR: column "employee.empname" must appear in the GROUP BY clause or be used in an aggregate function A única diferença entre duas consultas é que estou usando uma subconsulta na última para limitar as linhas antes de ingressar. Como resolvo esse erro?

Programador
fonte

Respostas:

21

O recurso do Postgres para poder usar a chave primária de uma tabela GROUP BYe não precisar adicionar as outras colunas dessa tabela na GROUP BYcláusula é relativamente novo e funciona apenas para tabelas base. O otimizador não é (ainda?) Inteligente o suficiente para identificar chaves primárias para exibições, ctes ou tabelas derivadas (como no seu caso).

Você pode adicionar as colunas que pretende na SELECTna GROUP BYcláusula:

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 use uma subconsulta (e transfira a mesma 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 ;

que também pode ser escrito como:

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 ;

Desde que você está na versão 9.3+. você também pode usar uma LATERALassociação:

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 ;
ypercubeᵀᴹ
fonte
@ypercude Obrigado. Esta é uma maneira simples e limpa.
Programmer