В чем разница между WHERE и HAVING в SQL?

Ответ

Ключевое различие между WHERE и HAVING заключается в этапе выполнения запроса, на котором происходит фильтрация, и в типе данных, к которым она применяется.

Критерий WHERE HAVING
Этап выполнения Фильтрует строки ДО группировки (GROUP BY). Фильтрует группы или агрегированные результаты ПОСЛЕ группировки (GROUP BY).
Применяется к Отдельным записям (строкам) исходной таблицы. Агрегированным значениям (результатам функций COUNT, SUM, AVG, MIN, MAX) или сгруппированным столбцам.
Использование с агрегатными функциями Не может содержать агрегатные функции (если только они не в подзапросе). Может и обычно содержит агрегатные функции.
Обязательность GROUP BY Может использоваться без GROUP BY. Требует наличия GROUP BY (кроме редких случаев агрегации по всей таблице).

Логический порядок выполнения запроса: FROMWHEREGROUP BYHAVINGSELECTORDER BY

Наглядные примеры:

-- Пример 1: WHERE (фильтрация строк перед группировкой)
-- Задача: Найти среднюю зарплату по отделам, но только для сотрудников с зарплатой > 50000.
SELECT 
    DepartmentID,
    AVG(Salary) AS AvgSalary
FROM Employees
WHERE Salary > 50000  -- Сначала отфильтровываем ВСЕХ сотрудников с низкой зарплатой
GROUP BY DepartmentID;
-- Здесь AVG() считается только по оставшимся (высокооплачиваемым) сотрудникам в каждом отделе.

-- Пример 2: HAVING (фильтрация после группировки)
-- Задача: Найти отделы, в которых средняя зарплата превышает 70000.
SELECT 
    DepartmentID,
    AVG(Salary) AS AvgSalary
FROM Employees
GROUP BY DepartmentID          -- Сначала группируем всех сотрудников по отделам и считаем среднюю зарплату
HAVING AVG(Salary) > 70000;   -- Затем отфильтровываем те группы (отделы), где средняя зарплата > 70000

-- Пример 3: Комбинирование WHERE и HAVING
-- Задача: Найти отделы с более чем 5 сотрудниками, но учитывать только сотрудников, принятых после 2020 года.
SELECT 
    DepartmentID,
    COUNT(*) AS EmployeeCount
FROM Employees
WHERE HireDate > '2020-01-01'  -- 1. Фильтруем строки: берем только новых сотрудников
GROUP BY DepartmentID          -- 2. Группируем оставшихся по отделам
HAVING COUNT(*) > 5;           -- 3. Фильтруем группы: оставляем отделы с >5 новыми сотрудниками

Простое правило:

  • Используйте WHERE, когда условие фильтрации относится к данным в исходной таблице (например, Salary, HireDate).
  • Используйте HAVING, когда условие фильтрации основано на результате вычисления по группе (например, COUNT(*), AVG(Salary)).

Ответ 18+ 🔞

Смотри, объясню на пальцах, а то эти учебники заебали всё усложнять. Разница между WHERE и HAVING — это как разница между «отсеять говно до того, как считать» и «посчитать, а потом отсеять то, что нас не устраивает».

Представь, что ты начальник цеха, а таблица Employees — это твои работяги.

WHERE — это когда ты стоишь на проходной и не пускаешь на территорию завода всяких левых.
Ты смотришь на каждого в отдельности: «Ты, Вася, зарплата меньше 50к? Нахуй иди, тебя в расчёт не берём. Ты, Петя, устроился в каменном веке? Тоже свободен».
То есть, ты сначала отфильтровываешь строки, а уж потом считаешь по оставшимся что-то общее — среднюю зарплату по цеху или количество человек. WHERE работает с отдельными записями, как с конкретными людьми.

-- Сначала выгоняем всех, кто получает меньше 50к, а потом считаем среднюю по отделам
SELECT DepartmentID, AVG(Salary)
FROM Employees
WHERE Salary > 50000  -- Стоим на проходной и проверяем каждого
GROUP BY DepartmentID;

HAVING — это когда ты уже всё посчитал, получил сводку по отделам, смотришь на цифры и говоришь: «А этот отдел — говно, выбросить».
Ты не можешь применить это условие к отдельному работяге, потому что оно касается итога по всей группе. Например, «оставить только те отделы, где средняя зарплата больше 70к» или «где больше пяти человек».
HAVING всегда идёт после GROUP BY и работает с агрегатамиCOUNT(), SUM(), AVG().

-- Сначала сгруппировали всех по отделам, посчитали среднюю, а потом...
SELECT DepartmentID, AVG(Salary)
FROM Employees
GROUP BY DepartmentID
HAVING AVG(Salary) > 70000; -- ...отфильтровали итоговые результаты: «Этот отдел — нищеброды, нахуй»

Проще запомнить так:

  • WHERE — фильтрует строки (отдельных людей).
  • HAVING — фильтрует группы (итоги по отделам).

Их можно и комбинировать, это вообще красота. Сначала отсеиваешь мусор на входе (WHERE), потом группируешь, а потом отсеиваешь негодные итоги (HAVING).

-- Берём только сотрудников, принятых после 2020 года (WHERE),
-- группируем по отделам,
-- и оставляем только те отделы, где таких новеньких больше пяти (HAVING).
SELECT DepartmentID, COUNT(*)
FROM Employees
WHERE HireDate > '2020-01-01'  -- Не пустили старых пердунов
GROUP BY DepartmentID
HAVING COUNT(*) > 5;           -- Отсеяли отделы, где новичков мало

Вот и вся магия. Главное — понять порядок: сначала WHERE выкидывает лишнее, потом GROUP BY кучкует, а HAVING уже эту сгруппированную кучу проверяет на соответствие. Если путаешь — представь того начальника на проходной, он всё расставит по местам.