SELECT: проекция, фильтрация, сортировка

Рейтинг: 45.3% · 9 голосов
Подробный курс по PostgreSQL: SQL по демобазе, типы данных и JSONB, индексы, транзакции и MVCC, VACUUM, EXPLAIN и планировщик, роли и привилегии, репликация, бэкап, настройка под нагрузку и под 1С. Актуально на 2026.
Ответить
Аватара пользователя
Egor_DBA
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

SELECT: проекция, фильтрация, сортировка

Сообщение Egor_DBA »

Оглавление курса (47)
  1. Что такое PostgreSQL: история, философия, где применяется
  2. Урок 1. Что нового в PostgreSQL 15 и далее (15 -> 16 -> 17)
  3. Установка и первый запуск: Linux, Windows, Docker
  4. psql детально: метакоманды и работа в консоли
  5. Демобаза Авиаперевозки Postgres Pro: структура и развёртывание
  6. SELECT: проекция, фильтрация, сортировка (вы здесь)
  7. Транзакции и ACID
  8. Уровни изоляции транзакций: Read Committed, Repeatable Read, Serializable
  9. Соединения таблиц (JOIN)
  10. Агрегация: GROUP BY, HAVING, GROUPING SETS
  11. Подзапросы и CTE (WITH), рекурсия
  12. Оконные функции
  13. Изменение данных: INSERT/UPDATE/DELETE, UPSERT, MERGE
  14. Числовые и булевы типы, NULL и трёхзначная логика
  15. Строки, текст, дата и время, таймзоны
  16. Массивы, диапазоны, enum, составные типы
  17. JSON и JSONB: операторы и индексация
  18. DDL: CREATE TABLE, схемы, ALTER
  19. Ограничения целостности
  20. Представления и материализованные представления
  21. Последовательности, IDENTITY, генерируемые столбцы
  22. Индексы: B-tree и когда индекс не используется
  23. Типы индексов: Hash, GiST, SP-GiST, GIN, BRIN
  24. Продвинутые индексы: частичные, по выражению, покрывающие
  25. Обслуживание индексов и таблиц: bloat, REINDEX, CONCURRENTLY
  26. Полнотекстовый поиск
  27. Роли и пользователи
  28. Привилегии: GRANT/REVOKE
  29. Row-Level Security и безопасность данных
  30. Подключение приложения: pg_hba.conf, SSL, пулы соединений
  31. Серверное программирование: функции и процедуры
  32. Триггеры и события
  33. Архитектура PostgreSQL: процессы и память
  34. MVCC: версии строк и видимость
  35. VACUUM, autovacuum, freeze и wraparound
  36. WAL, контрольные точки и долговечность
  37. Блокировки и взаимоблокировки
  38. Планировщик запросов
  39. EXPLAIN и EXPLAIN ANALYZE: чтение планов
  40. Конфигурация сервера: ключевые параметры
  41. Настройка под нагрузку и под 1С
  42. Параллельные запросы и партиционирование
  43. Резервное копирование и восстановление
  44. Репликация: потоковая и hot standby
  45. Логическая репликация и кластерные решения
  46. Внешние данные, расширения и сертификация
  47. Разбор планов запросов на explain.tensor.ru: глубокое чтение EXPLAIN ANALYZE
Урок 5. SELECT: проекция, фильтрация, сортировка

Запрос SELECT - это первое, с чего начинается работа с любой базой данных, и одновременно то, к чему сводится большая часть всей нагрузки на сервер. В этом уроке мы разберём три кита чтения данных в postgresql: какие столбцы вернуть (проекция), какие строки оставить (фильтрация в WHERE) и в каком порядке их показать (ORDER BY). По дороге трогаем DISTINCT, псевдонимы, LIMIT/OFFSET и операторы поиска по тексту. Все примеры - на демобазе Авиаперевозки от Postgres Professional, схема bookings, где есть рейсы и аэропорты.

Изображение

Как это работает

Полезно держать в голове логический порядок, в котором СУБД осмысливает запрос, потому что он не совпадает с порядком, в котором мы его пишем. Сначала берётся источник из FROM, затем строки прореживаются условием WHERE, потом считаются группировки, дальше остаётся проекция в списке SELECT, после неё убираются дубли при DISTINCT, и только в самом конце применяются ORDER BY и LIMIT. Именно поэтому псевдоним столбца, заданный в SELECT, нельзя использовать внутри WHERE - на момент фильтрации его ещё не существует, а вот в ORDER BY уже можно.

Проекция - это выбор столбцов. Звёздочка возвращает все столбцы таблицы, но в реальном коде её лучше избегать: явный список устойчив к изменению структуры и не тащит лишние данные по сети. Любому выражению в списке можно дать имя через AS, и это имя попадёт в заголовок результата.

Фильтрация живёт в WHERE и работает построчно: для каждой строки выражение даёт истину, ложь или NULL, и в результат проходят только те, где получилась истина. Здесь работают обычные операторы сравнения, логические AND, OR, NOT, а также удобные сокращения: IN для перечисления значений, BETWEEN для диапазона включительно, LIKE и ILIKE для поиска по шаблону. ILIKE - это регистронезависимый вариант LIKE, расширение PostgreSQL, в стандарте SQL его нет.

Отдельно про NULL и трёхзначную логику. NULL означает не ноль и не пустую строку, а отсутствие значения. Сравнение с ним через знак равенства всегда даёт неопределённость, поэтому проверять надо через IS NULL и IS NOT NULL. Это самая частая ловушка новичка, и мы вернёмся к ней в граблях.

Сортировка ORDER BY задаёт порядок строк: ASC по возрастанию (по умолчанию), DESC по убыванию. Поскольку в выдаче могут быть NULL, важно решить, куда их девать: NULLS FIRST или NULLS LAST. По умолчанию при ASC значения NULL идут последними, при DESC - первыми. LIMIT ограничивает число строк, OFFSET пропускает первые N, и почти всегда их применяют вместе с ORDER BY, иначе порядок строк не определён и пагинация поедет.

SQL и примеры

Проекция с псевдонимами. Берём номер рейса, время вылета и статус, переименовывая столбцы под удобный заголовок:

Код: Выделить всё

SELECT flight_no AS рейс,
       scheduled_departure AS вылет_по_плану,
       status
FROM flights
LIMIT 10;
Фильтрация по конкретному значению и диапазону. Рейсы из Шереметьево (код SVO), вылетающие в определённое окно дат, через BETWEEN:

Код: Выделить всё

SELECT flight_no, scheduled_departure, arrival_airport
FROM flights
WHERE departure_airport = 'SVO'
  AND scheduled_departure BETWEEN '2017-08-01' AND '2017-08-02';
Перечисление через IN читается понятнее цепочки OR. Рейсы, прилетающие в один из трёх московских аэропортов:

Код: Выделить всё

SELECT flight_no, departure_airport, arrival_airport, status
FROM flights
WHERE arrival_airport IN ('SVO', 'DME', 'VKO')
  AND status <> 'Cancelled';
Поиск по тексту через ILIKE без оглядки на регистр. Найдём аэропорты, в названии города которых встречается часть подстроки, символ процента - любое число любых знаков:

Код: Выделить всё

SELECT airport_code, city, timezone
FROM airports
WHERE city ILIKE '%new%'
ORDER BY city ASC;
Сортировка с управлением положением NULL. Покажем рейсы и фактическое время вылета так, чтобы ещё не вылетевшие (с NULL в actual_departure) оказались в конце:

Код: Выделить всё

SELECT flight_no, scheduled_departure, actual_departure
FROM flights
ORDER BY actual_departure DESC NULLS LAST
LIMIT 20;
DISTINCT убирает повторы. Список уникальных аэропортов вылета - по сути все коды, откуда вообще были рейсы:

Код: Выделить всё

SELECT DISTINCT departure_airport
FROM flights
ORDER BY departure_airport;
Пагинация через LIMIT и OFFSET. Вторая страница по 10 рейсов, отсортированных по времени вылета:

Код: Выделить всё

SELECT flight_no, scheduled_departure
FROM flights
ORDER BY scheduled_departure, flight_id
LIMIT 10 OFFSET 10;
Обратите внимание: во вторую сортировку добавлен flight_id как разрыватель ничьих, чтобы страницы не пересекались при одинаковом времени вылета.

Частые грабли
  • Сравнение с NULL через знак равенства. WHERE actual_departure = NULL не вернёт ничего и не выдаст ошибки. Нужно WHERE actual_departure IS NULL.
  • NOT IN со списком, где может оказаться NULL. Если внутри подзапроса встретится NULL, весь NOT IN отдаёт пустоту из-за трёхзначной логики. Безопаснее NOT EXISTS.
  • LIKE 'abc%' использует индекс, а LIKE '%abc' - нет: ведущий процент не даёт опереться на обычный btree, поиск идёт перебором.
  • Псевдоним из SELECT в WHERE. Из-за порядка вычислений алиас в WHERE не виден, будет ошибка column does not exist; в ORDER BY он работает.
  • LIMIT без ORDER BY. Без явной сортировки порядок строк не гарантирован, и пагинация выдаёт то дубли, то пропуски.
  • Большой OFFSET тормозит: сервер всё равно читает и отбрасывает пропущенные строки. Для глубоких страниц лучше keyset-пагинация по WHERE на ключе.
  • BETWEEN включает обе границы. Для дат-времени это часто ловушка: верхняя граница цепляет полночь следующего дня.
Мини-лаба
  • Подключитесь к демобазе: psql -d demo. Проверьте схему командой \dt bookings.*
  • Выведите 15 рейсов с псевдонимами столбцов на русском: номер, плановый вылет, статус.
  • Отберите рейсы из аэропорта своего города (узнайте код через SELECT по airports с ILIKE по city).
  • Постройте фильтр с IN на три аэропорта прилёта и условием статус не Cancelled.
  • Отсортируйте рейсы по actual_departure так, чтобы NULL были в конце, и ограничьте 20 строками.
  • Соберите DISTINCT по парам departure_airport и arrival_airport - получите уникальные маршруты.
  • Сделайте третью страницу пагинации по 25 строк через LIMIT/OFFSET с устойчивой сортировкой.
Контрольные вопросы
  • Почему псевдоним столбца можно указать в ORDER BY, но нельзя в WHERE?
  • Чем ILIKE отличается от LIKE и есть ли ILIKE в стандарте SQL?
  • Как ведут себя NULL при сортировке по умолчанию для ASC и для DESC?
  • Почему шаблон LIKE '%text' обычно не использует btree-индекс, а LIKE 'text%' использует?
  • В чём опасность NOT IN, если в списке значений может оказаться NULL?
  • Почему большой OFFSET медленный и чем его заменить для глубоких страниц?
👍4 ❤️1 🔥2 😄 🤔2
Аватара пользователя
Omega59
Сообщения: 1
Зарегистрирован: 14 май 2026, 20:13

Re: SELECT: проекция, фильтрация, сортировка

Сообщение Omega59 »

А почему у меня WHERE actual_departure = NULL молча отдаёт ноль строк, без ошибки? Полчаса искал опечатку, пока не дошло про IS NULL.
👍1 ❤️1 🔥 😄 🤔
Аватара пользователя
Kmaximus
Сообщения: 1
Зарегистрирован: 19 май 2026, 00:08

Re: SELECT: проекция, фильтрация, сортировка

Сообщение Kmaximus »

На больших OFFSET выборка реально проседает - переписал постраничку через WHERE по времени вылета и стало мгновенно.
👍1 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Демобаза Авиаперевозки Postgres Pro: структура и развёртывание
Следующая глава →
Транзакции и ACID

Все главы курса «PostgreSQL: от первого запроса до продакшена»

Поделиться темой: ✈ Telegram VK

Вернуться в «PostgreSQL: от первого запроса до продакшена»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 2 гостя