← Назад к вопросам

Работает ли SQL WHERE с агрегатными функциями

1.0 Junior🔥 101 комментариев
#Работа с данными

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

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Можно ли использовать агрегатные функции в WHERE?

Нет, агрегатные функции (AVG, SUM, COUNT, MIN, MAX и т.д.) НЕЛЬЗЯ использовать напрямую в условии WHERE. Это фундаментальное ограничение SQL, возникающее из-за логического порядка выполнения запроса.

Почему WHERE не работает с агрегатами?

Причина: Клауза WHERE выполняется ДО группировки и вычисления агрегатных функций. Её задача — фильтровать отдельные строки перед их объединением в группы.

Порядок выполнения SQL-запроса (упрощённо):

  1. FROM/JOIN - выбор таблиц
  2. WHERE - фильтрация строк на уровне отдельных записей (здесь агрегаты ещё не известны!)
  3. GROUP BY - группировка отфильтрованных строк
  4. HAVING - фильтрация на уровне групп (после вычисления агрегатов)
  5. SELECT - выбор полей, вычисление выражений
  6. ORDER BY - сортировка

Поскольку на этапе WHERE данные ещё не сгруппированы, СУБД не может вычислить агрегатные значения для несуществующих групп.

Как фильтровать по результатам агрегатных функций?

Для этой задачи используется специальная клауза HAVING, которая выполняется ПОСЛЕ GROUP BY.

Пример сравнения WHERE и HAVING

Допустим, у нас есть таблица sales:

CREATE TABLE sales (
    id INTEGER,
    department TEXT,
    amount DECIMAL
);

НЕПРАВИЛЬНО (вызовет ошибку):

-- Попытка найти отделы с суммой продаж > 10000
SELECT department, SUM(amount) as total
FROM sales
WHERE SUM(amount) > 10000  -- ОШИБКА: агрегат в WHERE!
GROUP BY department;

ПРАВИЛЬНО через HAVING:

-- Найти отделы, чья общая сумма продаж превышает 10000
SELECT department, SUM(amount) as total
FROM sales
GROUP BY department
HAVING SUM(amount) > 10000;  -- Фильтрация ПОСЛЕ группировки

Комбинирование WHERE и HAVING

Можно использовать обе клаузы в одном запросе: WHERE фильтрует исходные строки, HAVING — сгруппированные результаты.

-- Найти отделы, чья сумма продаж ЗА ТЕКУЩИЙ МЕСЯЦ превышает 5000
SELECT department, SUM(amount) as monthly_total
FROM sales
WHERE sale_date >= '2024-01-01'  -- Фильтр строк до группировки
  AND sale_date < '2024-02-01'
GROUP BY department
HAVING SUM(amount) > 5000;  -- Фильтр групп после агрегации

Альтернативные подходы

1. Подзапрос в FROM

SELECT *
FROM (
    SELECT department, SUM(amount) as total
    FROM sales
    GROUP BY department
) AS department_totals
WHERE total > 10000;  -- Здесь WHERE работает, т.к. агрегат уже вычислен

2. Оконные функции с подзапросом

SELECT *
FROM (
    SELECT department, 
           SUM(amount) OVER (PARTITION BY department) as dept_total
    FROM sales
) AS t
WHERE dept_total > 10000;

3. CTE (Common Table Expression)

WITH department_totals AS (
    SELECT department, SUM(amount) as total
    FROM sales
    GROUP BY department
)
SELECT *
FROM department_totals
WHERE total > 10000;

Ключевые различия WHERE vs HAVING

АспектWHEREHAVING
Время выполненияДо группировкиПосле группировки
Агрегатные функцииНельзя использоватьМожно и нужно использовать
Уровень работыС отдельными строкамиС группами строк
Синтаксический порядокПосле FROM, до GROUP BYПосле GROUP BY, до ORDER BY
ИндексированиеМожет использовать индексыОбычно не использует индексы напрямую

Практический совет

Простое правило запоминания: Если вам нужно отфильтровать данные до вычисления статистики — используйте WHERE. Если нужно отфильтровать результаты статистических вычислений — используйте HAVING.

Пример для Android-разработчика: При работе с Room Persistence Library или прямыми SQL-запросами в Android, это различие особенно важно, так как неправильное использование вызовет исключение SQLiteException. Всегда проверяйте запросы с агрегатными функциями на соответствие этому правилу.