HAVING SQL: шпаргалка для собеседования

Проверь себя · 1/3разбор после ответа
Вы хотите сравнить текущую метрику с метрикой следующего периода во временном ряду. Какая функция возвращает «следующее» значение относительно текущей строки по порядку ORDER BY?

Зачем HAVING

HAVING фильтрует группы после агрегации. Ровно для случая «покажи только группы, где условие на агрегат выполнено». На собесе это вечный вопрос: HAVING vs WHERE.

Базовый синтаксис

SELECT col, agg_func(x)
FROM TABLE
WHERE row_condition
GROUP BY col
HAVING group_condition;
  • WHERE — фильтр строк до группировки.
  • GROUP BY — сворачивает строки в группы.
  • HAVING — фильтр групп после агрегации.

HAVING vs WHERE

-- WHERE фильтрует строки
SELECT city, COUNT(*) FROM users
WHERE created_at >= '2026-01-01'  -- фильтр на строки
GROUP BY city;

-- HAVING фильтрует группы
SELECT city, COUNT(*) FROM users
GROUP BY city
HAVING COUNT(*) > 100;  -- фильтр на агрегат

Главное правило: в WHERE не может быть агрегата — он ещё не посчитан. WHERE COUNT(*) > 100 — синтаксическая ошибка.

Порядок выполнения

FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY

Отсюда всё:

  • В WHERE нельзя алиасы из SELECT — SELECT ещё не выполнился.
  • В WHERE нельзя агрегаты — GROUP BY ещё впереди.
  • В HAVING можно всё, кроме ORDER BY.

Если хочется сразу закрепить тему на практике — открой тренажёр в Telegram. 10 минут в день — и синтаксис в пальцах.

Когда фильтровать в WHERE, а когда в HAVING

Пример 1: фильтр по значению столбца

-- ✅ WHERE (фильтр строк, быстрее — не группируем лишнее)
SELECT city, COUNT(*) FROM users WHERE country = 'RU' GROUP BY city;

Пример 2: фильтр по агрегату

-- ✅ HAVING
SELECT city, COUNT(*) FROM users GROUP BY city HAVING COUNT(*) > 10;

Пример 3: и то, и другое

SELECT city, COUNT(*) FROM users
WHERE country = 'RU'         -- фильтр строк
GROUP BY city
HAVING COUNT(*) > 10;         -- фильтр групп

Правило оптимизации: фильтруйте в WHERE всё, что можно. Это уменьшает объём данных для GROUP BY.

HAVING без GROUP BY

Можно — если в SELECT только агрегаты:

SELECT COUNT(*), AVG(amount) FROM orders HAVING SUM(amount) > 1000000;

Вернёт либо одну строку (если условие выполнено), либо ничего. На практике почти не используется.

Прокачай SQL для собеса
500+ задач по SQL: оконные функции, JOIN, CTE — с разбором каждой
Тренировать SQL в Telegram

Несколько условий в HAVING

SELECT city, COUNT(*) AS users, SUM(revenue) AS rev
FROM orders
GROUP BY city
HAVING COUNT(*) > 10 AND SUM(revenue) > 100000;

Комбинируйте через AND/OR.

HAVING с подзапросами

SELECT category, SUM(revenue)
FROM orders JOIN products USING (product_id)
GROUP BY category
HAVING SUM(revenue) > (
    SELECT AVG(cat_rev) FROM (
        SELECT SUM(revenue) AS cat_rev FROM orders JOIN products USING (product_id)
        GROUP BY category
    ) t
);

«Категории с выручкой выше средней по категориям». На senior-собесах спрашивают.

Частые ловушки

1. Агрегат в WHERE

-- ❌ Ошибка
SELECT city FROM users WHERE COUNT(*) > 100 GROUP BY city;

-- ✅ Правильно
SELECT city FROM users GROUP BY city HAVING COUNT(*) > 100;

2. Алиас в HAVING

-- PostgreSQL — можно
SELECT city, COUNT(*) AS cnt FROM users GROUP BY city HAVING cnt > 10;

-- MySQL — тоже можно
-- SQL Server — НЕЛЬЗЯ, нужно повторить выражение
SELECT city, COUNT(*) AS cnt FROM users GROUP BY city HAVING COUNT(*) > 10;

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

3. HAVING для фильтра по столбцу

-- ❌ Некрасиво и медленно
SELECT city, COUNT(*) FROM users GROUP BY city HAVING city = 'Moscow';

-- ✅ Правильно
SELECT city, COUNT(*) FROM users WHERE city = 'Moscow' GROUP BY city;

Чтобы не только читать теорию, но и решать реальные задачи — загляните в бот Карьерника. Там по каждой теме подборка вопросов с разборами.

10 задач на HAVING

1. Города с более 100 пользователей

SELECT city, COUNT(*) FROM users GROUP BY city HAVING COUNT(*) > 100;

2. Пользователи с более 3 заказами

SELECT user_id FROM orders GROUP BY user_id HAVING COUNT(*) > 3;

3. Товары с суммарной выручкой более 1М

SELECT product_id, SUM(amount) FROM orders GROUP BY product_id HAVING SUM(amount) > 1000000;

4. Категории с более 10 уникальных покупателей

SELECT category, COUNT(DISTINCT user_id) FROM orders JOIN products USING (product_id)
GROUP BY category HAVING COUNT(DISTINCT user_id) > 10;

5. Дни с выручкой выше среднего по месяцу

WITH daily AS (
    SELECT created_at::DATE AS day, SUM(amount) AS rev FROM orders GROUP BY 1
)
SELECT day, rev FROM daily
WHERE rev > (SELECT AVG(rev) FROM daily);

Через HAVING с подзапросом — чуть длиннее. Через WHERE + CTE — чище.

6. Клиенты, купившие все три продукта

SELECT user_id
FROM orders
WHERE product_id IN ('A', 'B', 'C')
GROUP BY user_id
HAVING COUNT(DISTINCT product_id) = 3;

7. Пользователи с минимум 2 платежами и суммой >5000

SELECT user_id FROM payments
GROUP BY user_id
HAVING COUNT(*) >= 2 AND SUM(amount) > 5000;

8. Города, где средний чек больше 2000

SELECT city FROM orders GROUP BY city HAVING AVG(amount) > 2000;

9. Группы, где присутствует хотя бы одна покупка > 10000

SELECT user_id FROM orders GROUP BY user_id HAVING MAX(amount) > 10000;

10. Пары (user, month), где заказов больше 10

SELECT user_id, DATE_TRUNC('month', created_at), COUNT(*)
FROM orders
GROUP BY 1, 2
HAVING COUNT(*) > 10;

Как тренироваться

HAVING — простая конструкция, но на собесе регулярно путают с WHERE. Особенно когда фильтр можно написать и там, и там (по столбцу группировки).

Тренажёр Карьерник содержит блок задач на HAVING и сочетание с WHERE, GROUP BY, подзапросами.

Совет: на собесе всегда проговаривайте порядок выполнения (FROM → WHERE → GROUP BY → HAVING). Это автоматически разводит путаницу между WHERE и HAVING.

Читайте также

FAQ

Можно ли HAVING без GROUP BY?

Можно — если в SELECT только агрегаты. Но такой запрос применим редко (либо 1 строка, либо 0). Обычно HAVING идёт в паре с GROUP BY.

Почему алиас из SELECT не работает в WHERE, но работает в HAVING?

Порядок выполнения: WHERE выполняется до SELECT, поэтому алиасы ещё не известны. HAVING выполняется после (условно говоря) — может ссылаться на алиасы в PostgreSQL и MySQL. В SQL Server — нет, повторяйте выражение.

Что эффективнее — WHERE или HAVING?

WHERE, если фильтр применим до группировки. Чем раньше отсечёте строки — тем меньше работы для GROUP BY. HAVING использовать только для условий на агрегаты.

HAVING в NULL

HAVING работает с NULL так же, как WHERE: условие HAVING col = NULL не сработает — используйте HAVING col IS NULL.