Shanraq.org Shanraq.org
SUM, COUNT, GROUP BY и HAVING: итог по категориям
IT

SUM, COUNT, GROUP BY и HAVING: итог по категориям

Сжимаем множество операций в проверяемые итоги, считаем строки отдельно от значений и находим категории, вышедшие за лимит.

Зачем это нужно

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

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

Сразу целиком

SELECT c.name,
       COUNT(*) AS operation_count,
       SUM(t.amount_tiyn) / 100.0 AS spent_kzt
FROM transactions AS t
JOIN categories AS c ON c.id = t.category_id
WHERE c.kind = 'expense'
GROUP BY c.id, c.name
HAVING SUM(t.amount_tiyn) >= 1000000
ORDER BY SUM(t.amount_tiyn) DESC;

Разбор

COUNT(*) считает строки. COUNT(note) считает только строки, где note не NULL. SUM, AVG, MIN, MAX игнорируют NULL; на пустом наборе SUM возвращает NULL, поэтому итог часто оборачивают в COALESCE.

WHERE отбирает строки до группировки. HAVING отбирает уже собранные группы. Условие по виду категории принадлежит WHERE, условие по сумме — HAVING.

В строгом переносимом SQL каждый неагрегированный столбец результата должен участвовать в GROUP BY. Некоторые СУБД допускают сокращения, но они могут вернуть произвольное значение из группы.

Карта урока

Строки собираются в группы и проходят HAVING

Скажите своими словами

  1. Чем COUNT(*) отличается от COUNT(note)?
  2. Где поставить условие kind = 'expense'?
  3. Что вернёт SUM для пустого набора?

Задание

Для каждой расходной категории покажите лимит, сумму сентября и остаток лимита. Оставьте только категории, у которых было хотя бы две операции.

Куда это встанет в проекте

Это центральная таблица отчёта: факт расходов сопоставляется с планом.

Ответы

Показать ответы
SELECT c.name,
       c.monthly_limit_tiyn / 100.0 AS limit_kzt,
       SUM(t.amount_tiyn) / 100.0 AS spent_kzt,
       (c.monthly_limit_tiyn - SUM(t.amount_tiyn)) / 100.0 AS left_kzt
FROM categories AS c
JOIN transactions AS t ON t.category_id = c.id
WHERE c.kind = 'expense'
  AND t.happened_on >= '2026-09-01' AND t.happened_on < '2026-10-01'
GROUP BY c.id, c.name, c.monthly_limit_tiyn
HAVING COUNT(*) >= 2;

Источники

Если вы нашли ошибку или опечатку в тексте статьи, то сообщите нам об этом

Проверить задание

Сначала решите и запустите в VS Code — редактор покажет ошибку на месте. Готовое решение вставьте сюда. Проверяет модель: она укажет на ошибку, но не даст готовый ответ.

Чтобы проверить, нужно войти. Войти

Комментарии (0)

Пока нет комментариев. Будьте первым.