Блог
SQL

SQL с нуля, часть 4: CASE, работа с датами, BETWEEN и DISTINCT

Это четвёртая часть серии «SQL с нуля». Уже разобрали основы, агрегаты и подзапросы и JOIN. В этой части — четыре инструмента, которые превращают простые выборки в отчёты, готовые к реальной работе.

CASE: условная логика внутри запроса

CASE — это if/else внутри SQL-запроса. Он вычисляет значение для каждой строки в зависимости от условия, прямо в SELECT.
SELECT
name,
age,
CASE
WHEN age < 25 THEN 'до 25'
WHEN age BETWEEN 25 AND 35 THEN '25-35'
ELSE '35+'
END AS age_group
FROM customers;

Условия проверяются по порядку сверху вниз, и срабатывает первое подходящее — как цепочка if / elif / else. ELSE необязателен: если ни одно условие не выполнилось и ELSE не указан, результат — NULL.
CASE особенно полезен в связке с агрегатами — например, чтобы посчитать несколько условных итогов в одном запросе вместо нескольких отдельных:
SELECT
COUNT(CASE WHEN amount > 3000 THEN 1 END) AS big_orders,
COUNT(CASE WHEN amount <= 3000 THEN 1 END) AS small_orders
FROM orders;

Здесь CASE возвращает 1 для подходящих строк и NULL для остальных, а COUNT() считает только не-NULL значения — получается подсчёт по условию без WHERE и без нескольких отдельных запросов.

BETWEEN: диапазон без двух условий

BETWEEN — короткая запись для «значение между X и Y включительно». Границы входят в диапазон.
-- Эти два запроса эквивалентны
SELECT name, age FROM customers WHERE age BETWEEN 25 AND 35;
SELECT name, age FROM customers WHERE age >= 25 AND age <= 35;

BETWEEN работает не только с числами, но и с датами и текстом (по алфавитному порядку):
SELECT * FROM orders
WHERE created_at BETWEEN '2026-01-01' AND '2026-01-31';

Частая ловушка с датами: если колонка хранит не только дату, но и время (2026-01-31 23:59:59 — это timestamp, а не просто дата), а верхняя граница указана как '2026-01-31', то она трактуется как полночь начала дня — '2026-01-31 00:00:00'. Все заказы, сделанные позже полуночи 31 января, из диапазона выпадут. Надёжнее в таком случае явно указывать конец дня ('2026-01-31 23:59:59') или использовать < со следующим днём (created_at < '2026-02-01').

Работа с датами: базовые функции

Даты — отдельная тема с диалектными различиями, но несколько операций нужны почти всегда.
Извлечь часть даты (год, месяц, день):
-- PostgreSQL
SELECT EXTRACT(YEAR FROM created_at) AS order_year,
EXTRACT(MONTH FROM created_at) AS order_month
FROM orders;

-- MySQL
SELECT YEAR(created_at) AS order_year,
MONTH(created_at) AS order_month
FROM orders;

Отчёт по месяцам — сгруппировать заказы по году и месяцу:
-- PostgreSQL
SELECT
DATE_TRUNC('month', created_at) AS month,
COUNT(*) AS orders_count,
SUM(amount) AS revenue
FROM orders
GROUP BY DATE_TRUNC('month', created_at)
ORDER BY month;

DATE_TRUNC('month', ...) обрезает дату до начала месяца — все заказы одного месяца получают одинаковое значение и группируются вместе. В MySQL прямого аналога нет, обычно группируют по паре YEAR(created_at), MONTH(created_at).
Разница между датами и текущая дата:
-- PostgreSQL: сколько дней прошло с заказа
SELECT name, CURRENT_DATE - created_at::date AS days_ago FROM orders;

-- MySQL: то же самое
SELECT name, DATEDIFF(CURDATE(), created_at) AS days_ago FROM orders;

Работа с датами — тема, где диалекты расходятся сильнее всего. Общий совет: если пишете запрос под конкретную СУБД, сверяйтесь с её документацией — синтаксис EXTRACT/DATE_TRUNC (PostgreSQL) и YEAR/MONTH/DATEDIFF (MySQL) не взаимозаменяем напрямую.

DISTINCT: убрать повторяющиеся строки

DISTINCT оставляет только уникальные строки результата.
SELECT DISTINCT city FROM customers;

Важный нюанс: DISTINCT применяется ко всей строке результата, а не к одной колонке, если колонок несколько.
-- Уникальные ПАРЫ (город, возрастная группа) — не то же самое, что уникальные города
SELECT DISTINCT city, age FROM customers;

Этот запрос вернёт больше строк, чем SELECT DISTINCT city, потому что уникальной считается комбинация city + age, а не только город.
DISTINCT часто путают с GROUP BY для похожей задачи «дай уникальные значения». Разница: DISTINCT только убирает повторы, GROUP BY создан для агрегатов. Если нужно посчитать что-то по группам — это GROUP BY, если просто получить список без дублей — DISTINCT читается яснее.

Частые вопросы про CASE, даты и DISTINCT в SQL

Чем CASE в SELECT отличается от WHERE с несколькими условиями? WHERE фильтрует — решает, какие строки попадут в результат. CASE в SELECT не убирает строки, а вычисляет для каждой из них новое значение. Это разные задачи: WHERE age > 30 уберёт строки с возрастом до 30, а CASE WHEN age > 30 THEN ... END оставит все строки, но добавит вычисленную колонку.
Входят ли границы в BETWEEN? Да, BETWEEN 25 AND 35 включает и 25, и 35. Это эквивалентно >= 25 AND <= 35, а не строгому неравенству.
Как правильно фильтровать по диапазону дат, если в колонке есть время? Не полагайтесь на BETWEEN 'дата1' AND 'дата2', если колонка типа timestamp — верхняя граница трактуется как начало дня. Надёжнее: created_at >= 'начало' AND created_at < 'начало_следующего_периода'.
В чём разница между DISTINCT и GROUP BY? Для задачи «просто уникальные значения одной колонки» — они дают одинаковый результат. GROUP BY при этом создан для агрегатов (COUNT, SUM по группам), DISTINCT — только убирает повторяющиеся строки, без вычислений.

Что дальше

CASE, BETWEEN, работа с датами и DISTINCT закрывают четвёртый этап дорожной карты — после них большинство практических отчётных задач становится по силам. Дальше — самая продвинутая тема серии: часть 5, оконные функции — нарастающие итоги, ранжирование внутри групп и сравнение с соседними строками.
Отработать темы этой части можно на тренажёре SQL Arena — задачи на условную логику, даты и уникальные значения встречаются во всех треках, от аналитики до бэкенда.

-------

Полезные ссылки школы

Сайт Quality Academy:
/main
Telegram-канал:
https://t.me/quality_academy
YouTube-канал:
https://www.youtube.com/@quality_academy
ВКонтакте:
https://vk.com/quality_academy
Канал отзывов учеников (58+ отзывов):
https://t.me/quality_academy_reviews
Задать вопрос менеджеру:
https://t.me/quality_academy_bot
Тест «Подойдёт ли вам тестирование»:
https://quiz.quality-academy.ru
5000+ вопросов с собесов тестировщика — бот-тренажёр:
https://t.me/quality_academy_interview_bot
3 практические задачи и дорожная карта:
https://t.me/quality_academy_tasks_bot
Бесплатные тренажёры — SQL Arena, Playwright Arena, Python Arena:
SQL Arena · Playwright Arena · Python Arena