Ошибка Postgres [столбец должен появляться в предложении GROUP BY или использоваться в статистической функции] при использовании подзапроса

16

У меня есть две таблицы employeeи phones. Сотрудник может иметь от 0 до n телефонных номеров. Я хочу перечислить имена сотрудников с их номерами телефонов. Я использую запрос ниже, который работает нормально.

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

введите описание изображения здесь

Таблица сотрудников может содержать большое количество строк. Я хочу получить только несколько сотрудников одновременно. Например, я хочу получить 3 сотрудников с их номерами телефонов. Я пытаюсь выполнить этот запрос.

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

Но я получаю эту ошибку. ERROR: column "employee.empname" must appear in the GROUP BY clause or be used in an aggregate function Единственная разница между двумя запросами состоит в том, что я использую подзапрос в последнем, чтобы ограничить количество строк перед соединением. Как мне решить эту ошибку?

Программист
источник

Ответы:

21

Функция Postgres, позволяющая использовать первичный ключ таблицы GROUP BYи не требующая добавления других столбцов этой таблицы в GROUP BYпредложении, является относительно новой и работает только для базовых таблиц. Оптимизатор (пока?) Не достаточно умен, чтобы идентифицировать первичные ключи для представлений, ctes или производных таблиц (как в вашем случае).

Вы можете добавить нужные столбцы SELECTв 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 ;

или используйте подзапрос (и перенесите 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 ;

который также может быть записан как:

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 ;

Так как вы в версии 9.3+. Вы также можете использовать 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 ;
ypercubeᵀᴹ
источник
@ypercude Спасибо. Это простой и чистый способ.
Программист