Stage 3. SELECT, фильтрация, сортировка и форма результата
В этой теме важно понять не только синтаксис, но и порядок мышления. SQL-запрос на чтение обычно строится как конвейер: сначала мы определяем, какие поля хотим видеть, затем указываем источник данных, потом отсекаем лишние строки, после этого задаем порядок, и только в конце ограничиваем размер ответа.
Базовый скелет запроса:
SELECT ...
FROM ...
WHERE ...
ORDER BY ...
LIMIT ...;
Ниже разберем каждый блок отдельно и подробно.
Блок 1. SELECT: форма результата и зачем она важна
SELECT определяет итоговую структуру ответа. По сути, вы описываете “какие колонки увидит клиент”.
Простой пример:
SELECT id, user_id, amount, status, created_at
FROM orders;
Этот запрос возвращает только перечисленные колонки. Это важно по нескольким причинам:
-
Меньше лишних данных. Если вы выбираете только нужные поля, запрос читает и передает меньше информации.
-
Понятный контракт. Если endpoint или отчет ожидает конкретные поля, явный
SELECTфиксирует этот контракт. -
Меньше риска утечки.
SELECT *может случайно включить служебные или чувствительные поля.
Частая ошибка новичков: использовать SELECT * “для удобства”. В обучении можно, но в рабочем коде лучше явный список полей.
Алиасы в SELECT
Когда имена длинные или конфликтуют, используйте AS:
SELECT
id AS order_id,
amount AS order_amount
FROM orders;
Алиасы делают результат читаемее и удобнее для frontend/DTO.
Блок 2. WHERE: что такое условие и как оно работает
WHERE — это фильтр строк. Он отвечает на вопрос: “Какие записи должны остаться?”.
Пример:
SELECT id, amount, status
FROM orders
WHERE status = 'PAID';
После такого фильтра в выдаче будут только оплаченные заказы.
Логические операторы
WHERE почти всегда строится из нескольких условий:
SELECT id, user_id, amount, status, created_at
FROM orders
WHERE status = 'PAID'
AND amount >= 500
AND created_at >= DATE '2026-05-01';
Здесь строка пройдет фильтр только если выполняются все три условия.
Можно использовать OR:
WHERE status = 'PAID' OR status = 'SHIPPED'
И IN как более удобную запись:
WHERE status IN ('PAID', 'SHIPPED')
WHERE и NULL
NULL нельзя проверять через =:
-- неправильно
WHERE paid_at = NULL
-- правильно
WHERE paid_at IS NULL
Это критично, потому что неправильная проверка часто дает “пустой результат” и ломает логику.
Порядок применения
WHERE отрабатывает до сортировки и до LIMIT. Поэтому фильтрация обычно сильнее всего влияет на производительность: чем раньше вы отсекли лишние строки, тем меньше работы дальше.
Блок 3. ORDER BY: зачем нужна сортировка
ORDER BY задает порядок строк в результате. Если сортировку не указать, порядок не гарантирован.
SELECT id, amount, created_at
FROM orders
WHERE status = 'PAID'
ORDER BY created_at DESC;
DESC — от новых к старым.
ASC — от старых к новым.
Почему одной сортировки иногда недостаточно
Если у двух строк одинаковый created_at, порядок между ними может “прыгать”. Для стабильности добавляют второй ключ:
ORDER BY created_at DESC, id DESC
Это особенно важно для пагинации.
Сортировка по тексту и числам
- Числа сортируются как числа (
100>20). - Текст сортируется по правилам collation (языковые правила могут отличаться).
Поэтому для пользовательских списков заранее определяйте, по какому полю и в каком направлении сортируете.
LIMIT: контроль объема результата
LIMIT ограничивает число возвращаемых строк.
SELECT id, user_id, amount, status, created_at
FROM orders
WHERE status = 'PAID'
ORDER BY created_at DESC, id DESC
LIMIT 5;
Смысл: после фильтра и сортировки вернуть только первые 5 строк.
Почему LIMIT нужен почти всегда:
- защищает API от слишком большого ответа;
- снижает нагрузку на сеть и клиент;
- делает выдачу предсказуемой для интерфейса.
Полный разбор одного запроса
SELECT id, user_id, amount, status, created_at
FROM orders
WHERE status = 'PAID'
ORDER BY created_at DESC, id DESC
LIMIT 5;
Как это выполняется логически:
- Берем таблицу
orders. - Оставляем только строки со статусом
PAID. - Сортируем оставшиеся строки от новых к старым.
- Если даты равны — сортируем по
id. - Возвращаем только первые 5 строк.
Пример результата
| id | user_id | amount | status | created_at |
|---|---|---|---|---|
| 15 | 2 | 900.00 | PAID | 2026-05-10 12:15 |
| 14 | 1 | 1200.00 | PAID | 2026-05-10 11:01 |
| 12 | 2 | 700.00 | PAID | 2026-05-10 10:40 |
| 9 | 1 | 450.00 | PAID | 2026-05-10 10:05 |
| 7 | 3 | 300.00 | PAID | 2026-05-10 09:58 |
Частые ошибки новичков
SELECT *вместо явного списка полей.- Отсутствие
ORDER BYв пользовательском списке. - Неверная работа с
NULL(= NULLвместоIS NULL). - Слишком широкий
WHERE(в ответе “слишком много мусора”). - Отсутствие
LIMITна больших таблицах.
Практика перед следующим уроком
Выполните шаги в этом порядке.
Шаг 1. Добавьте тестовые заказы:
INSERT INTO orders(user_id, amount, status, created_at) VALUES
(1, 1200.00, 'PAID', '2026-05-10 12:00:00'),
(1, 300.00, 'NEW', '2026-05-10 11:00:00'),
(2, 700.00, 'PAID', '2026-05-10 10:00:00'),
(2, 950.00, 'PAID', '2026-05-10 09:00:00'),
(3, 150.00, 'CANCELLED', '2026-05-10 08:00:00');
Шаг 2. Запустите базовый запрос без фильтра:
SELECT id, user_id, amount, status, created_at
FROM orders;
Шаг 3. Добавьте фильтрацию:
SELECT id, user_id, amount, status, created_at
FROM orders
WHERE status = 'PAID';
Шаг 4. Добавьте сортировку и ограничение:
SELECT id, user_id, amount, status, created_at
FROM orders
WHERE status = 'PAID'
ORDER BY created_at DESC, id DESC
LIMIT 5;
Что проверить в конце:
- остались только
PAID; - сверху самая новая дата;
- не больше 5 строк.