WHERE в SQL — полный гайд по фильтрации данных

Коротко

WHERE -- оператор фильтрации строк в SQL. Без него любой SELECT возвращает всю таблицу целиком, а аналитику почти всегда нужна только часть данных: заказы за последний месяц, активные пользователи, события с определённым статусом. На собеседовании по SQL WHERE -- первое, что проверяют, потому что фильтрация лежит в основе любого запроса.

Синтаксис WHERE

SELECT column1, column2
FROM table_name
WHERE condition

WHERE стоит после FROM и выполняется до GROUP BY, HAVING и ORDER BY. Строки, для которых условие даёт TRUE, попадают в результат. Строки с FALSE или NULL -- отбрасываются.

Операторы сравнения

Оператор Значение Пример
= Равно WHERE status = 'active'
<> или != Не равно WHERE status <> 'deleted'
> Больше WHERE amount > 1000
< Меньше WHERE age < 30
>= Больше или равно WHERE rating >= 4.5
<= Меньше или равно WHERE attempts <= 3

Все операторы работают со строками, числами и датами. Строки сравниваются лексикографически (по алфавиту с учётом регистра).

AND, OR, NOT и приоритет операторов

AND и OR комбинируют несколько условий. NOT инвертирует условие.

SELECT *
FROM users
WHERE status = 'active'
  AND created_at >= '2025-01-01'

Приоритет: AND выполняется раньше OR. Это главный источник ошибок. Без скобок запрос работает не так, как кажется:

-- НЕПРАВИЛЬНО: AND связывает status и channel, OR "висит" отдельно
WHERE channel = 'organic' OR channel = 'referral' AND status = 'active'
-- Эквивалентно: channel = 'organic' OR (channel = 'referral' AND status = 'active')

-- ПРАВИЛЬНО: скобки явно задают логику
WHERE (channel = 'organic' OR channel = 'referral') AND status = 'active'

Правило: если в WHERE есть и AND, и OR -- всегда ставьте скобки. Даже если приоритет правильный, скобки делают запрос читаемым.

IN -- список значений или подзапрос

IN проверяет, входит ли значение в набор:

-- список значений
SELECT *
FROM orders
WHERE status IN ('paid', 'shipped', 'delivered')

-- подзапрос
SELECT *
FROM users
WHERE user_id IN (
    SELECT user_id
    FROM orders
    WHERE amount > 10000
)

IN (список) -- короткая альтернатива цепочке OR. NOT IN -- инвертирует. Осторожно с NULL в списке: NOT IN (1, 2, NULL) вернёт пустой результат, потому что x <> NULL даёт NULL, а не TRUE.

BETWEEN -- диапазон значений

SELECT *
FROM orders
WHERE amount BETWEEN 1000 AND 5000

BETWEEN включает обе границы -- эквивалентно amount >= 1000 AND amount <= 5000. Работает с числами, датами и строками.

IS NULL / IS NOT NULL

NULL -- это «нет значения», и обычные операторы сравнения с ним не работают:

-- НЕПРАВИЛЬНО: всегда вернёт 0 строк
WHERE city = NULL

-- ПРАВИЛЬНО
WHERE city IS NULL
WHERE city IS NOT NULL

= NULL не равно IS NULL. Любое сравнение с NULL через =, <>, > даёт NULL (не TRUE и не FALSE). Это одна из самых частых ошибок на собеседовании.

LIKE -- поиск по шаблону

LIKE фильтрует строки по шаблону с подстановочными символами:

-- email на gmail.com
WHERE email LIKE '%@gmail.com'

-- имя из 4 букв, начинается на 'А'
WHERE name LIKE 'А___'

% -- любое количество символов, _ -- ровно один. Подробнее -- в гайде по LIKE.

WHERE с датами

Даты -- один из самых частых фильтров в аналитике:

-- события за последние 7 дней
SELECT *
FROM events
WHERE created_at >= CURRENT_DATE - INTERVAL '7 days'

-- события за конкретный месяц
SELECT *
FROM events
WHERE created_at >= '2025-03-01'
  AND created_at < '2025-04-01'

Для диапазонов дат используйте >= и < (не BETWEEN) -- так вы исключаете начало следующего периода и не теряете записи с временем 23:59:59.

WHERE с агрегатами -- нельзя! Используйте HAVING

WHERE фильтрует строки до группировки. Поэтому использовать агрегатные функции в WHERE нельзя:

-- ОШИБКА: WHERE не знает про COUNT
SELECT department, COUNT(*) AS cnt
FROM employees
WHERE COUNT(*) > 5
GROUP BY department

-- ПРАВИЛЬНО: HAVING фильтрует после группировки
SELECT department, COUNT(*) AS cnt
FROM employees
GROUP BY department
HAVING COUNT(*) > 5

Порядок выполнения: FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY. WHERE работает до GROUP BY, HAVING -- после.

Практические примеры

Активные пользователи за период:

SELECT user_id, username, last_active_at
FROM users
WHERE status = 'active'
  AND last_active_at >= CURRENT_DATE - INTERVAL '30 days'

Заказы с непустым промокодом в определённом диапазоне сумм:

SELECT order_id, amount, promo_code
FROM orders
WHERE amount BETWEEN 500 AND 10000
  AND promo_code IS NOT NULL

Пользователи из конкретных городов, исключая тестовые аккаунты:

SELECT user_id, email, city
FROM users
WHERE city IN ('Москва', 'Санкт-Петербург', 'Казань')
  AND email NOT LIKE '%@test.%'

Типичные ошибки

= NULL вместо IS NULL. WHERE city = NULL всегда возвращает 0 строк. NULL -- не значение, а отсутствие значения. Для проверки на NULL используйте только IS NULL / IS NOT NULL.

OR без скобок. WHERE a = 1 OR b = 2 AND c = 3 -- AND выполнится первым. Результат: a = 1 OR (b = 2 AND c = 3), а не (a = 1 OR b = 2) AND c = 3. Всегда ставьте скобки.

NOT IN с NULL. WHERE id NOT IN (1, 2, NULL) вернёт пустой результат. Если в подзапросе может быть NULL -- используйте NOT EXISTS или добавьте WHERE ... IS NOT NULL в подзапрос.

BETWEEN с датами и временем. WHERE created_at BETWEEN '2025-03-01' AND '2025-03-31' пропустит записи 31 марта с временем после 00:00:00. Используйте >= '2025-03-01' AND < '2025-04-01'.

Вопросы с собеседований

-- Чем WHERE отличается от HAVING? -- WHERE фильтрует строки до группировки, HAVING -- после. В WHERE нельзя использовать агрегатные функции (COUNT, SUM и т.д.), в HAVING -- можно. Подробнее: WHERE vs HAVING.

-- Почему WHERE column = NULL не работает? -- Потому что любое сравнение с NULL через = возвращает NULL, а не TRUE. NULL означает «неизвестно», и неизвестно = неизвестно -- это не TRUE. Для проверки используется IS NULL.

-- В каком порядке выполняются части SQL-запроса? -- FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT. WHERE выполняется вторым -- сразу после определения источника данных.

-- Чем IN отличается от EXISTS? -- IN сравнивает значение со списком. EXISTS проверяет, вернул ли подзапрос хотя бы одну строку. На больших подзапросах EXISTS может быть быстрее, потому что останавливается при первом совпадении. Главное отличие: NOT IN ломается при NULL в подзапросе, NOT EXISTS -- нет.

-- Напишите запрос: пользователи, зарегистрировавшиеся в январе 2025 из Москвы или Питера, которые сделали хотя бы один заказ. -- SELECT u.user_id, u.name FROM users u WHERE u.city IN ('Москва', 'Санкт-Петербург') AND u.created_at >= '2025-01-01' AND u.created_at < '2025-02-01' AND EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.user_id).

Потренируйтесь в тренажёре

WHERE -- фундамент SQL, и на собеседовании его проверяют не отдельно, а в связке с JOIN, GROUP BY и подзапросами. Потренируйтесь писать запросы с фильтрацией -- откройте тренажёр с 200+ задачами по SQL и разборами.

FAQ

Можно ли использовать алиас из SELECT в WHERE?

Нет. WHERE выполняется до SELECT, поэтому алиас ещё не существует. WHERE total_amount > 1000 не сработает, если total_amount -- алиас. Повторите выражение: WHERE SUM(amount) > 1000 -- тоже нельзя (агрегат в WHERE). Вынесите в подзапрос или используйте HAVING.

WHERE лучше, чем HAVING, для фильтрации?

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

Как фильтровать по нескольким условиям одного столбца?

Используйте IN вместо цепочки OR: WHERE status IN ('active', 'pending', 'trial') -- короче и читаемее, чем WHERE status = 'active' OR status = 'pending' OR status = 'trial'. Для диапазонов -- BETWEEN или >= и <=.

Влияет ли порядок условий в WHERE на производительность?

В теории -- нет. Оптимизатор СУБД сам решает, в каком порядке проверять условия и какие индексы использовать. На практике стоит ставить наиболее селективное условие первым -- не ради скорости, а ради читаемости.

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

WHERE встречается в каждом SQL-запросе, и на собеседовании вас будут проверять на нюансах: NULL, приоритет AND/OR, даты с временем. В тренажёре Карьерник есть задачи на фильтрацию с разборами -- тренируйтесь по 15 минут в день, и ошибки уйдут.

Больше вопросов по SQL -- в примерах вопросов по всем темам.