Раздел 10 · Databases

GROUP BY и агрегатные функции

junior~15 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Этот урок открывает кластер про агрегацию. GROUP BY встречается почти в каждой задаче на собеседовании: «посчитай по отделам», «найди сумму по клиентам». Сам синтаксис знают все, а спотыкаются на одном правиле: что можно писать в SELECT рядом с группировкой. Запросы урока работают на учебной базе блока.

Как спрашивают

Что делает GROUP BY? Почему запрос SELECT department_id, name, max(salary) FROM employees GROUP BY department_id падает в PostgreSQL?

Короткий ответ

GROUP BY раскладывает строки по группам с одинаковым значением ключа группировки, а агрегатные функции сворачивают каждую группу в одно значение. После группировки от группы остаётся одна строка, поэтому в SELECT можно писать только колонки группировки и агрегаты. Колонка name в группе своя у каждого сотрудника, и СУБД не знает, какое имя показать.

Разбор: конверты с чеками

Представь, что у тебя коробка чеков, и ты раскладываешь их по конвертам с подписью отдела. Потом на каждом конверте пишешь итог: сколько чеков, общая сумма, самый крупный. На конверте помещаются подпись и итоги. Написать на конверте «имя из чека» нельзя: чеков внутри несколько, имена разные.

SELECT department_id,
       count(*)    AS employees,
       sum(salary) AS payroll,
       max(salary) AS top
FROM employees
GROUP BY department_id
ORDER BY department_id;
department_id | employees | payroll | top
--------------+-----------+---------+-------
            1 |         3 |  800000 | 300000
            2 |         2 |  350000 | 200000
            3 |         1 |  120000 | 120000
         NULL |         1 |  180000 | 180000

Обрати внимание на последнюю строку. У Жанны нет отдела, и все строки с department_id IS NULL попали в одну группу. В WHERE два NULL не равны друг другу, как мы разбирали в уроке про NULL, а GROUP BY складывает их вместе: для группировки NULL считается одним значением.

Без GROUP BY агрегат считается по всей таблице и возвращает одну строку:

SELECT avg(salary)::int AS avg_salary, min(hired_at), max(hired_at)
FROM employees;
avg_salary | min        | max
-----------+------------+-----------
    207143 | 2018-11-20 | 2024-01-15

дальше после входа

Урок целиком открыт всем, кто вошёл. Войди по своему email, чтобы читать дальше.

Вход пока по приглашению. Если хочешь учиться, напиши Vassiliy.