Выборка данных в SQL: разбор для новичка
В PostgreSQL 16 запрос SELECT помогает не просто получить строки, а точно задать, какие данные нужны и в каком порядке. Разберём фильтры, сортировку, группировку и типичные ошибки, из-за которых результат оказывается не тем, что вы ожидали.

Запрос SELECT может вернуть правильные строки, но в неверном порядке, посчитать не те записи или незаметно отбросить строки с NULL. Ниже — разбор выборки на PostgreSQL 16: на примерах таблицы orders вы увидите, как составлять запрос и проверять результат. Эти приёмы применимы и в других СУБД, но отдельные функции и правила могут отличаться.
Сначала задайте вопрос к данным
Допустим, в orders хранятся заказы с колонками id, customer_id, status, created_at и total. Перед запросом сформулируйте, что именно хотите получить: например, «последние десять оплаченных заказов дороже 1000». Тогда части запроса можно проверить по отдельности: какие колонки нужны, какие строки подходят, как их упорядочить и сколько показать.
В примерах предполагается, что таблица уже существует, а названные колонки имеют подходящие типы. Запросы используют синтаксис PostgreSQL 16; кавычки вокруг имён не нужны, если имена созданы без кавычек и не совпадают с зарезервированными словами.
- Доступ к базе и право читать таблицу orders.
- Подключение к PostgreSQL 16 через любой клиент, например psql или графическую оболочку.
- Понимание, какие значения хранятся в status и total. Если в таблице нет подходящих записей, запрос вернёт пустой результат — это не обязательно ошибка.
SELECT: какие колонки возвращать
Начните с явного перечня колонок. Так понятнее результат, меньше лишних данных уходит клиенту и ниже риск, что добавление колонки в таблицу неожиданно изменит поведение приложения.
SELECT id, customer_id, status, total
FROM orders;
Запрос возвращает перечисленные поля каждой строки orders. Он не обещает никакого порядка: если порядок важен, задайте его через ORDER BY. Запись SELECT * удобна для быстрой проверки таблицы, но в прикладном запросе обычно хуже явного списка: вы получаете все колонки, даже если нужны две.
WHERE: какие строки оставить
Чтобы выбрать только оплаченные заказы дороже 1000, добавьте условие. Строковые значения в SQL пишут в одинарных кавычках.
SELECT id, created_at, total
FROM orders
WHERE status = 'paid'
AND total > 1000;
Условия можно соединять через AND и OR. AND требует, чтобы совпали оба условия, а OR — чтобы совпало хотя бы одно. Скобки особенно важны при их сочетании: без скобок приоритет операций может дать не тот набор строк, который вы задумали.
SELECT id, status, total
FROM orders
WHERE (status = 'paid' OR status = 'shipped')
AND total > 1000;
Здесь сначала отбираются оплаченные или отправленные заказы, а затем среди них остаются заказы дороже 1000. Если убрать скобки, сочетание AND и OR может изменить смысл запроса.
Почему NULL не равен пустоте
NULL означает отсутствие известного значения. Сравнение status = NULL не находит такие строки: проверяйте их через IS NULL, а наличие значения — через IS NOT NULL.
SELECT id, status
FROM orders
WHERE status IS NULL;
Проверка «не равно» тоже требует внимания: условие status <> 'paid' не включает строки, где status равен NULL. Для них результат сравнения неизвестен, а WHERE оставляет только строки, где условие истинно.
ORDER BY и LIMIT: порядок и размер результата
Чтобы получить десять последних заказов, одного LIMIT недостаточно: без сортировки база вправе вернуть любые десять строк. Укажите сортировку и добавьте уникальный признак для разрешения совпадений по времени.
SELECT id, created_at, total
FROM orders
WHERE status = 'paid'
ORDER BY created_at DESC, id DESC
LIMIT 10;
DESC задаёт убывающий порядок, ASC — возрастающий; ASC используется по умолчанию. Если несколько заказов имеют одинаковое created_at, сортировка только по этой колонке не задаёт порядок между ними. Второй ключ id делает результат предсказуемее, если id уникален.
LIMIT ограничивает число возвращаемых строк, но не ускоряет запрос автоматически. Если подходящих записей много, база всё равно может проделать заметную работу, чтобы найти и отсортировать нужные. Для больших таблиц время выполнения зависит от объёма данных, условий, индексов и плана запроса.
GROUP BY: считать группы, а не строки
Когда нужно узнать число заказов каждого статуса, используйте группировку и агрегатную функцию. COUNT(*) считает строки в группе.
SELECT status, COUNT(*) AS order_count
FROM orders
GROUP BY status
ORDER BY order_count DESC;
После группировки запрос возвращает по строке на каждое значение status. NULL тоже образует отдельную группу. Если нужны только статусы с числом заказов больше десяти, условие ставят после группировки через HAVING:
SELECT status, COUNT(*) AS order_count
FROM orders
GROUP BY status
HAVING COUNT(*) > 10
ORDER BY order_count DESC;
WHERE фильтрует исходные строки до группировки, HAVING — уже сформированные группы. Если перепутать их, запрос либо не выполнится, либо ответит на другой вопрос.
Четыре проверки перед тем, как доверять результату
- Проверьте фильтр. Выполните запрос только с WHERE и посмотрите несколько строк. Убедитесь, что сравниваете правильные значения и типы данных.
- Проверьте пропуски. Если в колонке возможен NULL, решите явно, включать ли такие строки. Обычное сравнение их не поймает.
- Задайте полный порядок. Если применяете LIMIT, добавьте ключ сортировки, который различает строки с одинаковым значением основной колонки.
- Сверьте агрегат. COUNT(*) считает строки, а не обязательно уникальных клиентов. Для подсчёта уникальных значений нужен отдельный вопрос и, в подходящем случае, COUNT(DISTINCT customer_id).
Если в запросе есть LIMIT, но нет ORDER BY, вы ограничили количество строк, а не выбрали «первые» строки в каком-либо надёжном смысле.
Термин: агрегатная функция1 — функция, которая сводит набор строк к одному значению, например COUNT(*) считает строки, а SUM(total) складывает значения total.
Где выборка ломается
Условие выглядит правильным, но строк нет
Проверьте реальные значения: статус может называться не paid, а, например, completed; в строках могут отличаться регистр или пробелы. Сначала выполните простой запрос к нескольким строкам без фильтра. Не исправляйте данные догадкой — сначала выясните, какие значения действительно записаны.
Статистика расходится с ожиданием
Проверьте, что именно вы считаете: строки, уникальных клиентов или заказы после фильтра. Затем посмотрите, не исключает ли WHERE значения из-за NULL и не отсекает ли HAVING целые группы. Для группировки полезно сначала вывести исходные значения и подсчитать их без дополнительных условий.
Запрос стал медленным на большой таблице
Не делайте вывод только по числу строк в ответе: LIMIT не гарантирует, что база прочитала лишь это число строк. В PostgreSQL изучайте план выполнения через EXPLAIN, а фактическое время и число обработанных строк измеряйте на реалистичных данных. EXPLAIN ANALYZE выполняет запрос, поэтому для запросов, меняющих данные, его нельзя запускать без понимания последствий.
Индекс может помочь конкретному фильтру или сортировке, но не является универсальным ускорителем: он занимает место и увеличивает работу при изменении данных. Не создавайте индекс наугад. Сначала выясните, какая часть запроса медленная и что показывает план.
Что делать дальше
Возьмите один реальный вопрос к базе и соберите запрос по частям: SELECT, FROM, WHERE, затем ORDER BY и LIMIT. Сверьте строки с исходными данными, отдельно проверьте NULL и случаи с одинаковыми значениями сортировки. Когда запрос станет медленным на реалистичном объёме, переходите к EXPLAIN и изучению индексов — это отдельная тема, здесь разбиралась только логика выборки.
1 Термин «агрегатная функция» используется в документации PostgreSQL для функций, которые вычисляют одно значение по набору входных строк.
IT-архитектор / SQL-оптимизатор: Поиск N+1, оптимизация запросов, code review, поиск багов.
Спросить за 20 ₽Источники: PostgreSQL 16: SELECT, PostgreSQL 16: функции агрегирования
Частые вопросы
Почему SELECT * иногда считают плохой практикой?
Почему условие status = NULL ничего не находит?
Можно ли использовать LIMIT без ORDER BY?
Чем WHERE отличается от HAVING?
Будут ли эти примеры работать в другой версии PostgreSQL или другой СУБД?
Почему запрос медленный, хотя он возвращает только десять строк?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.