SELECT, WHERE, ORDER BY, LIMIT
↑ 1.1 Реляционная модель и SQL · ← Предыдущая · → Следующая
Зачем это на собесе
База любого SQL-собеседования: порядок выполнения запроса и работа с фильтрами.
Синтаксис
SELECT DISTINCT c.email, o.total AS order_total
FROM orders o
JOIN customers c ON c.id = o.customer_id
WHERE o.status = 'paid'
AND o.created_at >= now() - interval '30 days'
ORDER BY o.total DESC, c.email
LIMIT 20 OFFSET 40;Логический порядок выполнения
FROM/JOINWHEREGROUP BYHAVINGSELECT(вычисление выражений,DISTINCT)ORDER BYLIMIT/OFFSET
Поэтому алиас из SELECT нельзя использовать в WHERE, но можно в ORDER BY.
Фильтры в WHERE
| Оператор | Пример |
|---|---|
| сравнения | =, <>, <, <=, >, >= |
| диапазон | BETWEEN 10 AND 20 (обе границы включаются) |
| набор | status IN ('new', 'paid') |
| шаблон | name LIKE 'Ив%', ILIKE (без регистра) |
| регулярные | ~, ~* (PostgreSQL) |
| NULL | IS NULL, IS NOT NULL, IS DISTINCT FROM |
| логика | AND, OR, NOT (приоритет: NOT > AND > OR, ставьте скобки) |
ORDER BY и LIMIT
- без
ORDER BYпорядок строк не гарантирован; LIMITбезORDER BYвозвращает произвольные строки;NULLS FIRST/NULLS LAST; по умолчанию в PostgreSQL NULL считаются наибольшими;OFFSETбольшого размера медленный (БД читает и отбрасывает строки): для пагинации используйте keyset:
SELECT * FROM orders
WHERE (created_at, id) < ($1, $2)
ORDER BY created_at DESC, id DESC
LIMIT 20;Полезные приёмы
SELECT DISTINCT ON (customer_id) customer_id, id, created_at -- последний заказ клиента (PostgreSQL)
FROM orders ORDER BY customer_id, created_at DESC;
SELECT count(*) FILTER (WHERE status = 'paid') AS paid, count(*) AS total FROM orders;
SELECT id, CASE WHEN total >= 1000 THEN 'крупный' ELSE 'обычный' END AS kind FROM orders;
SELECT coalesce(nickname, name) FROM users;Оптимизация
- не используйте
SELECT *в продакшн-коде: лишние данные, ломает covering-индексы; - условие на индексированный столбец без функции:
WHERE created_at >= ..., а неWHERE date(created_at) = ...; LIKE '%abc'не использует обычный B-tree, для поиска подстроки нуженpg_trgm+ GIN.
Вопросы с ответами
Почему алиас из SELECT нельзя использовать в WHERE?
WHEREвыполняется раньшеSELECT; на этом шаге алиас ещё не существует. Используйте выражение целиком, подзапрос или CTE.
Как получить последние N записей на каждую группу?
DISTINCT ON, оконная функцияROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...)с фильтромrn <= NилиLATERALподзапрос.
Чем плох OFFSET на больших значениях?
Приходится прочитать и отбросить все пропущенные строки; keyset-пагинация использует индекс и работает за постоянное время.