Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. SELECT с нуля: чтение, фильтрация и порядок
sql_basics

SELECT с нуля: чтение, фильтрация и порядок

Структура запроса, NULL, поиск, сортировка и выдача данных страницами

Открыть лабораториюv1.1.0Запускается локально из публичного репозитория

SELECT с нуля: чтение, фильтрация и порядок строк

SQL — язык, на котором клиент описывает, какие данные хочет получить или изменить. В этом уроке работаем с SELECT: командой чтения данных.

SQL называют декларативным языком. Вы описываете нужный результат, а PostgreSQL сам выбирает способ его получить. Например, запрос говорит «покажи товары дороже 1000 рублей», но не диктует, в каком порядке читать файлы таблицы.

#Первый запрос

Следующий запрос читает таблицу marketplace.products:

SELECT p.id, p.sku, p.name, p.price, p.price * 1.20 AS price_with_tax FROM marketplace.products AS p;
  • FROM указывает источник строк;
  • SELECT перечисляет столбцы результата;
  • AS p задаёт таблице короткое имя;
  • AS price_with_tax даёт имя вычисленному столбцу.

Короткое имя называют псевдонимом (alias). Оно не переименовывает объект в базе и действует только внутри запроса.

SELECT * выбирает все столбцы. Это удобно для знакомства с таблицей в psql, но в коде приложения лучше перечислять столбцы: добавление нового поля не изменит ответ незаметно.

#В каком порядке понимать SELECT

Запрос пишут начиная с SELECT, но логически PostgreSQL рассматривает его части в другом порядке:

FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT

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

Внутри сервер может выполнить работу иначе ради скорости, но результат обязан соответствовать этому смыслу.

#WHERE: какие строки оставить

WHERE содержит условие отбора. Такое условие называют предикатом.

SELECT id, name, price FROM marketplace.products WHERE price >= 1000;

В результат попадут только строки, для которых условие равно TRUE.

#NULL — значение неизвестно или отсутствует

NULL не равен пустой строке, нулю или слову «нет». Он означает, что значения нет либо оно неизвестно. Проверять его нужно через IS NULL:

SELECT id FROM app_users WHERE email IS NULL;

Выражение email = NULL даёт не TRUE и не FALSE, а третье логическое значение UNKNOWN — «неизвестно». WHERE оставляет только TRUE, поэтому такая запись не найдёт строки.

Когда два значения нужно сравнить так, чтобы NULL считался обычным отдельным состоянием, используется IS DISTINCT FROM:

SELECT old_value IS DISTINCT FROM new_value AS changed;

У NOT IN есть связанная ловушка: один NULL в проверяемом наборе может сделать итог неизвестным. Для поиска строк без соответствия обычно надёжнее NOT EXISTS; он подробно разобран в уроке о подзапросах.

#Временной диапазон

BETWEEN включает обе границы. Для выборки событий за март удобнее явно включить начало и исключить начало следующего месяца:

SELECT id, occurred_at FROM app_events WHERE occurred_at >= TIMESTAMPTZ '2026-03-01 00:00+00' AND occurred_at < TIMESTAMPTZ '2026-04-01 00:00+00';

Такой полуоткрытый интервал не зависит от точности времени и не посчитает полночь 1 апреля одновременно в двух месяцах.

#Поиск по шаблону

LIKE сравнивает текст с шаблоном. Символ % означает любое количество символов, а _ — ровно один. ILIKE делает то же без учёта регистра.

SELECT id, name FROM products WHERE name ILIKE 'postgres%';

Запрос найдёт строки, которые начинаются с postgres. Если % и _ введены пользователем и должны означать сами символы, их нужно экранировать. Параметризованный запрос защищает от внедрения SQL-кода, но не отменяет особый смысл символов шаблона.

Поиск вида '%term%' обычный B-tree-индекс чаще всего не ускоряет. Для поиска по фрагментам позже разберём расширение pg_trgm, а для поиска по словам — полнотекстовый поиск.

#Удаление повторов

DISTINCT оставляет только разные сочетания выбранных значений:

SELECT DISTINCT org_id, status FROM orders;

Если две строки имеют одинаковые org_id и status, в результате останется одна. DISTINCT не исправляет ошибочную связь таблиц и не должен использоваться как универсальное средство «убрать лишние строки».

Конструкция PostgreSQL DISTINCT ON выбирает первую строку каждой группы. Нужно явно задать, что значит «первая»:

SELECT DISTINCT ON (order_id) order_id, status, created_at, id FROM payments ORDER BY order_id, created_at DESC, id DESC;

Так запрос выбирает последний платёж заказа. id разрешает ничью, если время двух платежей совпало.

#ORDER BY: порядок нужно задавать

Без ORDER BY PostgreSQL не обещает никакого порядка. Сегодня строки могут выглядеть отсортированными, а после обновления, нового плана или создания индекса порядок изменится.

SELECT id, created_at, status FROM orders ORDER BY created_at DESC, id DESC LIMIT 50;

DESC означает порядок от большего к меньшему. Второй столбец id делает порядок полным: даже строки с одинаковым created_at располагаются однозначно. Если столбец допускает NULL, выберите NULLS FIRST или NULLS LAST.

#Как выдавать данные страницами

LIMIT 50 OFFSET 1000 означает «пропусти 1000 строк и верни следующие 50». Чем дальше страница, тем больше строк сервер должен найти и отбросить. Кроме того, новые и удалённые строки сдвигают границы страниц.

Для последовательной выдачи используют продолжение от последней увиденной строки. Этот способ называют пагинацией по ключу (keyset pagination):

SELECT id, created_at, status FROM orders WHERE org_id = $1 AND (created_at, id) < ($2, $3) ORDER BY created_at DESC, id DESC LIMIT 50;

$1, $2 и $3 — параметры, которые безопасно передаст приложение. Вторая страница получает время и id последней строки первой страницы. Для такого запроса подходит B-tree-индекс (org_id, created_at DESC, id DESC).

OFFSET остаётся нормальным выбором для небольших служебных списков и прямого перехода к условному номеру страницы, если объём невелик.

#Практика

В лаборатории 05 вы построите страницу каталога с однозначным порядком и вычисляемой ценовой категорией. Проверка сравнит не только набор, но и точную последовательность идентификаторов.

Перед следующим уроком убедитесь, что можете объяснить:

  1. Почему email = NULL не работает как проверка отсутствия значения?
  2. Почему без ORDER BY нельзя полагаться на порядок строк?
  3. Зачем к created_at в сортировке добавляют id?
  4. Чем продолжение по ключу отличается от OFFSET?

Подробнее: SELECT, сравнение значений, поиск по шаблону.

Далее: Как безопасно менять структуру базы