column X must appear in the GROUP BY clause or be used in an aggregate function

SELECT version();
                                                           version                                                           
-----------------------------------------------------------------------------------------------------------------------------
 PostgreSQL 14.5 (Debian 14.5-1.pgdg110+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit

Есть таблицы:

CREATE TABLE levels (
    id SERIAL PRIMARY KEY,
    title VARCHAR(61) NOT NULL
);

CREATE TABLE contents (
    id SERIAL PRIMARY KEY,
    title VARCHAR(255),
    level_id INT NOT NULL REFERENCES levels(id)
);

В них есть какие-то записи, я пытаюсь выполнить такой запрос:

WITH contents2 AS (
    SELECT
        c.*,
        (SELECT l.title FROM levels l WHERE l.id=c.level_id) AS level_name
    FROM contents c
)
SELECT c.id, c.title, c.level_name
FROM contents2 c
GROUP BY c.id

На что postgres говорит мне:

ERROR:  column "c.title" must appear in the GROUP BY clause or be used in an aggregate function

Я понимаю что значит эта ошибка, таким образом postgres говорит что для той или иной колонки в SELECT есть несколько вариантов для выдачи. Но в данном случае, абсолютно очевидно, что должно быть в c.title, там просто нет других вариантов, но почему он выдаёт мне эту ошибку? Мне надо писать каждую колонку в GROUP BY чтобы он не выкидывал эту ошибку?


Ответы (2 шт):

Автор решения: SwaD

т.к. СУБД не понимает, что делать с этим полем, необходимо явно указать/

Необходимо все перечисляемые поля добавить в GROUP BY(по этим полям группируем данные)

WITH contents2 AS (
    SELECT
        c.*,
        (SELECT l.title FROM levels l WHERE l.id=c.level_id) AS level_name
    FROM contents c
)
SELECT c.id, c.title, c.level_name
FROM contents2 c
GROUP BY c.id, c.title, c.level_name

Поля, которые не перечислены в GROUP BY можно использовать в запросе, но только вместе с агрегатными функциями

WITH contents2 AS (
    SELECT
        c.*,
        (SELECT l.title FROM levels l WHERE l.id=c.level_id) AS level_name
    FROM contents c
)
SELECT c.id, count(c.title), count(c.level_name), count(*)
FROM contents2 c
GROUP BY c.id
→ Ссылка
Автор решения: pegoopik

Попробуйте:

WITH contents2 AS (
    SELECT
        c.*,
        (SELECT l.title FROM levels l WHERE l.id=c.level_id) AS level_name
    FROM contents c
)
SELECT c.id, MAX(c.title) AS title, MAX(c.level_name) AS level_name
FROM contents2 c
GROUP BY c.id

Выглядит как костыль, но на самом деле довольно часто используется и не захламляет предложение GROUP BY

UPD: для искушённых. Сложность алгоритма сортировки +- O(N*Ln(N)) - поиск максимума O(N), т.е. MAX в предложении SELECT не только увеличивает читабельность кода, но и освобождает оптимизатор сервера от ненужных действий. Да, в MySQL по умолчанию оно и так сработает, но на мой взгляд это бага, а не фича.

→ Ссылка