WHERE SQL. Введение
WHERE — оператор фильтрации строк в SQL. Именно он сужает набор данных перед агрегированием, сортировкой и соединениями.
По запросу where sql обычно ищут способы строить точные условия: равенство/неравенство, диапазоны, проверка на NULL, поиск по подстроке, фильтрация по датам, а также использование подзапросов с EXISTS/IN и логики AND/OR/NOT.
Ниже — 100 унифицированных примеров для всех реализаций SQL.
Синтаксис
SELECT
столбцы
FROM
источник
WHERE
условие_фильтрации;
Типовая конструкция
SELECT
o.order_id,
o.customer_id,
o.total_amount
FROM
orders AS o
WHERE
o.status = 'PAID'
AND o.order_date >= DATE '2025-01-01'
ORDER BY
o.total_amount DESC;
Базовые условия: равенство и сравнения
Фильтр оплаченных заказов по статусу:
SELECT
o.order_id,
o.status
FROM
orders AS o
WHERE
o.status = 'PAID';Цена выше порога — числовое сравнение:
SELECT
p.product_id,
p.price
FROM
products AS p
WHERE
p.price > 100;Рейтинг не ниже 8 — граничное условие:
SELECT
f.film_id,
f.rating
FROM
films AS f
WHERE
f.rating >= 8;Исключаем тестовые записи — неравенство:
SELECT
u.user_id,
u.email
FROM
users AS u
WHERE
u.is_test <> 1;Курс валюты равен USD — точное совпадение:
SELECT
a.account_no,
a.currency
FROM
accounts AS a
WHERE
a.currency = 'USD';Складской остаток меньше минимума — сигнал пополнения:
SELECT
s.product_id,
s.qty_on_hand
FROM
stock AS s
WHERE
s.qty_on_hand < 10;Баланс неотрицательный — проверка нижней границы:
SELECT
a.account_no,
a.balance
FROM
account_balances AS a
WHERE
a.balance >= 0;Статус доставки из набора готовых значений через IN:
SELECT
d.delivery_id,
d.status
FROM
deliveries AS d
WHERE
d.status IN ('SHIPPED','DELIVERED');Сумма операции в диапазоне через BETWEEN:
SELECT
tx.tx_id,
tx.amount
FROM
transactions AS tx
WHERE
tx.amount BETWEEN 100 AND 500;Исключаем нулевые продажи — явное сравнение:
SELECT
o.order_id,
o.total_amount
FROM
orders AS o
WHERE
o.total_amount > 0;Комбинации условий: AND, OR, NOT
Оплачено и сумма больше 100 — двойной фильтр:
SELECT
o.order_id,
o.total_amount
FROM
orders AS o
WHERE
o.status = 'PAID'
AND o.total_amount > 100;Активные пользователи из выбранной страны или города:
SELECT
u.user_id,
u.country,
u.city
FROM
users AS u
WHERE
u.is_active = 1
AND (u.country = 'DE' OR u.city = 'Berlin');Не архив и не удалено — отрицание признаков:
SELECT
p.post_id,
p.title
FROM
posts AS p
WHERE
NOT p.is_archived = 1
AND NOT p.is_deleted = 1;Отбор категорий кроме скрытых — NOT с IN:
SELECT
c.category_id,
c.category
FROM
categories AS c
WHERE
c.category NOT IN ('Hidden','Internal');Цены вне диапазона — комбинированное условие:
SELECT
p.product_id,
p.price
FROM
products AS p
WHERE
p.price < 10 OR p.price > 1000;Срочные или просроченные задачи:
SELECT
t.task_id,
t.priority,
t.due_date
FROM
tasks AS t
WHERE
t.priority = 'HIGH'
OR t.due_date < CURRENT_DATE;Клиенты без VIP и без блокировок:
SELECT
c.customer_id,
c.is_vip,
c.is_blocked
FROM
customers AS c
WHERE
c.is_vip = 0
AND c.is_blocked = 0;Фильтр по нескольким брендам и активному флагу:
SELECT
p.product_id,
p.brand
FROM
products AS p
WHERE
p.brand IN ('A','B','C')
AND p.is_active = 1;Логическое ветвление по статусу и типу:
SELECT
t.ticket_id,
t.status,
t.type
FROM
tickets AS t
WHERE
(t.status = 'OPEN' AND t.type = 'INCIDENT')
OR (t.status = 'PENDING' AND t.type = 'REQUEST');Отрицание шаблона имени файла — NOT с LIKE:
SELECT
f.file_id,
f.file_name
FROM
files AS f
WHERE
f.file_name NOT LIKE 'tmp_%';Поиск по подстроке: LIKE и шаблоны
Email на домене example.com — правый шаблон:
SELECT
u.user_id,
u.email
FROM
users AS u
WHERE
u.email LIKE '%@example.com';Артикулы, начинающиеся с ABC — левый шаблон:
SELECT
p.product_id,
p.sku
FROM
products AS p
WHERE
p.sku LIKE 'ABC%';Поиск слов внутри заголовка (оба края):
SELECT
b.book_id,
b.title
FROM
books AS b
WHERE
b.title LIKE '%data%';Регистронезависимый поиск псевдонимом — нормализация:
SELECT
a.author_id,
a.nickname
FROM
authors AS a
WHERE
LOWER(a.nickname) LIKE '%dev%';Фильтр по телефону без масок — только цифры:
SELECT
c.contact_id,
c.phone
FROM
contacts AS c
WHERE
REGEXP_REPLACE(c.phone, '[^0-9]', '') LIKE '7%';Поиск пути файлов по расширению:
SELECT
f.file_id,
f.path
FROM
files AS f
WHERE
f.path LIKE '%.csv';Товары с пометкой PRO в названии:
SELECT
p.product_id,
p.title
FROM
products AS p
WHERE
p.title LIKE '% PRO %';Имена с дефисом в середине — маска:
SELECT
u.user_id,
u.full_name
FROM
users AS u
WHERE
u.full_name LIKE '%-%';Адреса, содержащие улицу Ленина:
SELECT
a.address_id,
a.full_address
FROM
addresses AS a
WHERE
a.full_address LIKE '%Ленина%';Фильтрация тегов по префиксу:
SELECT
t.tag_id,
t.tag
FROM
tags AS t
WHERE
t.tag LIKE 'sql_%';Проверки на NULL: IS NULL / IS NOT NULL
Почта отсутствует — отбор незаполненных адресов:
SELECT
u.user_id,
u.email
FROM
users AS u
WHERE
u.email IS NULL;Только заполненные телефоны — исключаем NULL:
SELECT
c.contact_id,
c.phone
FROM
contacts AS c
WHERE
c.phone IS NOT NULL;Дата доставки ещё не назначена:
SELECT
o.order_id,
o.delivery_date
FROM
orders AS o
WHERE
o.delivery_date IS NULL;Сотрудники с указанным менеджером:
SELECT
e.emp_id,
e.manager_id
FROM
employees AS e
WHERE
e.manager_id IS NOT NULL;Оценка отсутствует — выявляем пробелы в данных:
SELECT
r.student_id,
r.score
FROM
results AS r
WHERE
r.score IS NULL;Исключить пустые категории и нули после нормализации:
SELECT
p.product_id,
p.category
FROM
products AS p
WHERE
NULLIF(TRIM(p.category), '') IS NOT NULL;Последний логин не зафиксирован — поиск проблемных аккаунтов:
SELECT
u.user_id,
u.last_login_at
FROM
users AS u
WHERE
u.last_login_at IS NULL;Комнаты без привязки к курсам:
SELECT
r.room_id,
r.room_no
FROM
rooms AS r
WHERE
r.course_id IS NULL;Номер страховки заполнен — фильтр обязательных полей:
SELECT
pt.patient_id,
pt.policy_no
FROM
patients AS pt
WHERE
pt.policy_no IS NOT NULL;Отключаем аномалии: отрицательные цены и NULL:
SELECT
p.product_id,
p.price
FROM
products AS p
WHERE
p.price IS NOT NULL
AND p.price >= 0;Фильтрация по датам и времени
Заказы за текущий месяц — нижняя граница:
SELECT
o.order_id,
o.order_date
FROM
orders AS o
WHERE
o.order_date >= DATE_TRUNC('month', CURRENT_DATE);Визиты за последние 7 дней:
SELECT
v.visit_id,
v.visit_date
FROM
visits AS v
WHERE
v.visit_date >= CURRENT_DATE - INTERVAL '7' DAY;Платежи за 2024 год — точные рамки:
SELECT
p.payment_id,
p.paid_at
FROM
payments AS p
WHERE
p.paid_at >= DATE '2024-01-01'
AND p.paid_at < DATE '2025-01-01';Сеансы длительнее 5 минут:
SELECT
s.session_id,
s.duration_min
FROM
sessions AS s
WHERE
s.duration_min > 5;События до контрольной даты:
SELECT
e.event_id,
e.created_at
FROM
events AS e
WHERE
e.created_at < DATE '2025-06-01';Инциденты, закрытые сегодня:
SELECT
i.incident_id,
i.closed_at
FROM
incidents AS i
WHERE
i.closed_at >= CURRENT_DATE
AND i.closed_at < CURRENT_DATE + INTERVAL '1' DAY;Уведомления, отправленные ночью (0–6):
SELECT
n.notification_id,
n.sent_at
FROM
notifications AS n
WHERE
EXTRACT(HOUR FROM n.sent_at) BETWEEN 0 AND 6;Доставки, назначенные на выходные:
SELECT
d.delivery_id,
d.scheduled_at
FROM
deliveries AS d
WHERE
EXTRACT(DOW FROM d.scheduled_at) IN (0,6);Посты, опубликованные в прошлом квартале:
SELECT
p.post_id,
p.published_on
FROM
posts AS p
WHERE
p.published_on >= DATE_TRUNC('quarter', CURRENT_DATE) - INTERVAL '1' QUARTER
AND p.published_on < DATE_TRUNC('quarter', CURRENT_DATE);Покупки, совершённые вечером (18–23):
SELECT
o.order_id,
o.order_time
FROM
orders AS o
WHERE
EXTRACT(HOUR FROM o.order_time) BETWEEN 18 AND 23;Подзапросы: IN, EXISTS, корреляции
Клиенты, делавшие заказы — фильтр через IN:
SELECT
c.customer_id,
c.full_name
FROM
customers AS c
WHERE
c.customer_id IN (
SELECT
o.customer_id
FROM
orders AS o
);Статьи, у которых есть комментарии — EXISTS:
SELECT
p.post_id,
p.title
FROM
posts AS p
WHERE
EXISTS (
SELECT
1
FROM
comments AS c
WHERE
c.post_id = p.post_id
);Товары, которых нет в поставках — NOT EXISTS:
SELECT
p.product_id,
p.title
FROM
products AS p
WHERE
NOT EXISTS (
SELECT
1
FROM
supply AS s
WHERE
s.product_id = p.product_id
);Фильмы со средним рейтингом > 8 — коррелированный подзапрос:
SELECT
f.film_id,
f.title
FROM
films AS f
WHERE
(
SELECT
AVG(r.rating)
FROM
reviews AS r
WHERE
r.film_id = f.film_id
) > 8;Курсы с ≥ 10 записавшихся — вложенный подсчёт:
SELECT
c.course_id,
c.title
FROM
courses AS c
WHERE
(
SELECT
COUNT(*)
FROM
enrollments AS e
WHERE
e.course_id = c.course_id
) >= 10;Сотрудники, у которых есть задачи в статусе OPEN:
SELECT
e.emp_id,
e.full_name
FROM
employees AS e
WHERE
EXISTS (
SELECT
1
FROM
tasks AS t
WHERE
t.owner_id = e.emp_id
AND t.status = 'OPEN'
);Аккаунты без транзакций за 30 дней:
SELECT
a.account_no
FROM
accounts AS a
WHERE
NOT EXISTS (
SELECT
1
FROM
transactions AS tx
WHERE
tx.account_no = a.account_no
AND tx.tx_date >= CURRENT_DATE - INTERVAL '30' DAY
);Пассажиры с билетами более чем у 2 авиакомпаний:
SELECT
p.passenger_id,
p.full_name
FROM
passengers AS p
WHERE
(
SELECT
COUNT(DISTINCT t.airline)
FROM
tickets AS t
WHERE
t.passenger_id = p.passenger_id
) > 2;Полисы, по которым были выплаты:
SELECT
pl.policy_id,
pl.plan_code
FROM
policies AS pl
WHERE
EXISTS (
SELECT
1
FROM
payouts AS po
WHERE
po.policy_id = pl.policy_id
);Проекты с исполнителями из разных отделов:
SELECT
pr.project_id,
pr.title
FROM
projects AS pr
WHERE
(
SELECT
COUNT(DISTINCT e.dept_id)
FROM
assignments AS a
INNER JOIN employees AS e
ON e.emp_id = a.emp_id
WHERE
a.project_id = pr.project_id
) >= 2;Фильтрация на фоне соединений: JOIN
Заказы магазина в выбранном городе:
SELECT
o.order_id,
s.city
FROM
orders AS o
INNER JOIN stores AS s
ON s.store_id = o.store_id
WHERE
s.city = 'Paris';Отзывы только по активным фильмам:
SELECT
r.review_id,
f.title
FROM
reviews AS r
INNER JOIN films AS f
ON f.film_id = r.film_id
WHERE
f.is_active = 1;Товары с остатком на конкретном складе:
SELECT
p.product_id,
s.qty_on_hand
FROM
products AS p
INNER JOIN stock AS s
ON s.product_id = p.product_id
WHERE
s.warehouse_id = 1;Сотрудники с задачами в статусе IN_PROGRESS:
SELECT
e.emp_id,
t.task_id
FROM
employees AS e
INNER JOIN tasks AS t
ON t.owner_id = e.emp_id
WHERE
t.status = 'IN_PROGRESS';Клиенты с оплатами по карте:
SELECT
c.customer_id,
p.payment_id
FROM
customers AS c
INNER JOIN payments AS p
ON p.customer_id = c.customer_id
WHERE
p.method = 'CARD';Доставки с задержкой более суток:
SELECT
d.delivery_id,
o.order_id
FROM
deliveries AS d
INNER JOIN orders AS o
ON o.order_id = d.order_id
WHERE
d.delivered_at - d.shipped_at > INTERVAL '1' DAY;Ученики, записанные на конкретный курс:
SELECT
s.student_id,
c.title
FROM
students AS s
INNER JOIN enrollments AS e
ON e.student_id = s.student_id
INNER JOIN courses AS c
ON c.course_id = e.course_id
WHERE
c.course_code = 'SQL101';Маршруты с активными рейсами сегодня:
SELECT
r.route_code,
f.flight_no
FROM
routes AS r
INNER JOIN flights AS f
ON f.route_id = r.route_id
WHERE
f.flight_date = CURRENT_DATE;Менеджеры с закрытыми сделками за месяц:
SELECT
u.user_id,
d.deal_id
FROM
users AS u
INNER JOIN deals AS d
ON d.owner_id = u.user_id
WHERE
d.closed_on >= DATE_TRUNC('month', CURRENT_DATE);Устройства с ошибками последней телеметрии:
SELECT
d.device_id,
t.state
FROM
devices AS d
INNER JOIN telemetry AS t
ON t.device_id = d.device_id
WHERE
t.state = 'ERR';Где фильтровать: до GROUP BY и после через HAVING
Исключить отменённые до подсчёта заказов:
SELECT
o.store_id,
COUNT(*) AS orders_cnt
FROM
orders AS o
WHERE
o.status <> 'CANCELLED'
GROUP BY
o.store_id;Фильтр по диапазону дат до агрегации:
SELECT
o.order_date,
SUM(o.total_amount) AS day_sum
FROM
orders AS o
WHERE
o.order_date >= CURRENT_DATE - INTERVAL '30' DAY
GROUP BY
o.order_date;Сначала отбросить неактивные товары, затем агрегировать:
SELECT
p.category,
AVG(p.price) AS avg_price
FROM
products AS p
WHERE
p.is_active = 1
GROUP BY
p.category;Отбор по месту — затем группировка:
SELECT
s.city,
COUNT(*) AS stores_cnt
FROM
stores AS s
WHERE
s.country = 'FR'
GROUP BY
s.city;Фильтр нулевых сумм перед GROUP BY:
SELECT
a.account_no,
COUNT(*) AS tx_cnt
FROM
transactions AS t
INNER JOIN accounts AS a
ON a.account_no = t.account_no
WHERE
t.amount > 0
GROUP BY
a.account_no;Группировка по дню — только оплаченные:
SELECT
o.order_date,
SUM(o.total_amount) AS day_sum
FROM
orders AS o
WHERE
o.status = 'PAID'
GROUP BY
o.order_date;Сравнение: отбор по сумме уже через HAVING:
SELECT
s.store_id,
SUM(o.total_amount) AS revenue
FROM
orders AS o
INNER JOIN stores AS s
ON s.store_id = o.store_id
GROUP BY
s.store_id
HAVING
SUM(o.total_amount) > 10000;Индикатор: только успешные логины в день:
SELECT
l.login_date,
COUNT(*) AS ok_logins
FROM
logins AS l
WHERE
l.is_success = 1
GROUP BY
l.login_date;Фильтр по валюте до свёртки:
SELECT
o.currency,
SUM(o.total_amount) AS sum_amount
FROM
orders AS o
WHERE
o.currency = 'EUR'
GROUP BY
o.currency;Смешанный подход: часть условий до, часть — после:
SELECT
c.customer_id,
SUM(o.total_amount) AS total_amount
FROM
customers AS c
LEFT JOIN orders AS o
ON o.customer_id = c.customer_id
WHERE
o.status = 'PAID'
GROUP BY
c.customer_id
HAVING
SUM(o.total_amount) >= 1000;Практика оптимизации условий
Диапазоны вместо функций над колонкой — SARGable:
SELECT
o.order_id,
o.order_date
FROM
orders AS o
WHERE
o.order_date >= DATE '2025-01-01'
AND o.order_date < DATE '2025-02-01';Нормализация регистра до LIKE — вычисление слева:
SELECT
u.user_id,
u.email
FROM
users AS u
WHERE
LOWER(u.email) LIKE '%@example.com';Избегаем ведущих подстановок, когда возможно:
SELECT
p.product_id,
p.title
FROM
products AS p
WHERE
p.title LIKE 'SQL%';Используем IN вместо больших цепочек OR:
SELECT
s.store_id,
s.city
FROM
stores AS s
WHERE
s.city IN ('Paris','Lyon','Marseille');NULL‑безопасные проверки через IS NOT NULL:
SELECT
c.customer_id,
c.phone
FROM
customers AS c
WHERE
c.phone IS NOT NULL;Оптимальные границы времени через BETWEEN:
SELECT
n.notification_id,
n.sent_at
FROM
notifications AS n
WHERE
n.sent_at BETWEEN CURRENT_DATE AND CURRENT_DATE + INTERVAL '1' DAY;Улучшение селективности фильтром по флагу:
SELECT
p.product_id,
p.is_active
FROM
products AS p
WHERE
p.is_active = 1;Сначала узкий фильтр, затем соединение:
SELECT
o.order_id,
o.store_id
FROM
orders AS o
WHERE
o.order_date >= CURRENT_DATE - INTERVAL '7' DAY;Композитные ключи — фильтр по обоим полям:
SELECT
s.store_id,
s.dept_id
FROM
store_departments AS s
WHERE
s.store_id = 10
AND s.dept_id = 5;Балансная проверка: исключаем нулевые и NULL одновременно:
SELECT
a.account_no,
a.balance
FROM
account_balances AS a
WHERE
a.balance IS NOT NULL
AND a.balance <> 0;Прикладные сценарии
E‑commerce: скидочные продажи по диапазону процента:
SELECT
o.order_id,
oi.product_id,
oi.discount_pct
FROM
orders AS o
INNER JOIN order_items AS oi
ON oi.order_id = o.order_id
WHERE
oi.discount_pct BETWEEN 10 AND 50;Финансы: подозрительные транзакции по сумме и типу:
SELECT
tx.tx_id,
tx.amount,
tx.kind
FROM
transactions AS tx
WHERE
tx.amount >= 10000
AND tx.kind = 'CASH';HR: кандидаты с опытом и знанием SQL:
SELECT
cand.candidate_id,
cand.full_name
FROM
candidates AS cand
WHERE
cand.years_exp >= 3
AND LOWER(cand.summary) LIKE '%sql%';Медицина: визиты без диагноза, но с оплатой:
SELECT
v.visit_id,
v.paid_amount
FROM
visits AS v
WHERE
v.diagnosis_code IS NULL
AND v.paid_amount > 0;Образование: экзамены с оценкой ниже проходного:
SELECT
r.student_id,
r.score
FROM
results AS r
WHERE
r.score < 60;IoT: датчики в состоянии ошибки за последние сутки:
SELECT
t.sensor_id,
t.state,
t.collected_at
FROM
telemetry AS t
WHERE
t.state = 'ERR'
AND t.collected_at >= CURRENT_DATE - INTERVAL '1' DAY;Логистика: доставки с превышением SLA:
SELECT
d.delivery_id,
d.delivered_at,
d.promised_at
FROM
deliveries AS d
WHERE
d.delivered_at > d.promised_at;Безопасность: IP с ≥ 10 неуспешными логинами:
SELECT
l.ip_address,
COUNT(*) AS failed_cnt
FROM
logins AS l
WHERE
l.is_success = 0
GROUP BY
l.ip_address
HAVING
COUNT(*) >= 10;CRM: сделки без владельца или с пустым статусом:
SELECT
d.deal_id,
d.status,
d.owner_id
FROM
deals AS d
WHERE
(d.owner_id IS NULL OR d.status IS NULL);Retail: товары в дефиците по складу:
SELECT
s.product_id,
s.warehouse_id,
s.qty_on_hand
FROM
stock AS s
WHERE
s.qty_on_hand < 5;Заключение
where sql — это ядро точной выборки данных. Стройте читаемые условия, используйте логические связки, обрабатывайте NULL явно, применяйте подзапросы с EXISTS/IN, фильтруйте до GROUP BY и оставляйте агрегатные ограничения для HAVING.
Соблюдайте выравнивание, поддерживайте индексацию и избегайте ненужных функций над колонками — это ускорит запросы и упростит поддержку.