SQL описывает, что нужно получить, а не как. Как именно достать строки, решает планировщик базы. Но чтобы писать запросы без сюрпризов, нужно знать логический порядок выполнения.
Логический порядок
Пишем мы в одном порядке, а база «думает» в другом:
SELECT name, price * 1.2 AS with_vat -- 5
FROM products -- 1
WHERE price > 100 -- 2
GROUP BY ... -- 3
HAVING ... -- 4
ORDER BY with_vat DESC -- 6
LIMIT 10 OFFSET 20; -- 7
FROM → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT/OFFSET.
Отсюда главное следствие: псевдоним из SELECT нельзя использовать в WHERE (на шаге WHERE его ещё нет), а в ORDER BY можно.
SELECT price * 1.2 AS with_vat FROM products WHERE with_vat > 100; -- ошибка
SELECT price * 1.2 AS with_vat FROM products ORDER BY with_vat; -- ок
WHERE и операторы
- Сравнения:
=,<>(или!=),<,>=. - Диапазон:
price BETWEEN 100 AND 200включает обе границы. - Список:
city IN ('Москва', 'Казань'). - Шаблон:
name LIKE 'Ан%'(%любая строка,_один символ). В PostgreSQLILIKEбез учёта регистра. - Логика:
ANDсильнееOR.a OR b AND cэтоa OR (b AND c). Ставьте скобки.
NULL: трёхзначная логика
NULL значит «неизвестно». Любое сравнение с NULL даёт не true и не false, а NULL (unknown). WHERE пропускает только строки, где условие true.
SELECT * FROM customers WHERE email = NULL; -- всегда 0 строк
SELECT * FROM customers WHERE email IS NULL; -- правильно
Ловушки:
WHERE city <> 'Москва'не вернёт строки сcity IS NULL.x NOT IN (1, 2, NULL)никогда не бываетtrue: результат пустой.IS DISTINCT FROMсравнивает с учётом NULL:NULL IS DISTINCT FROM NULLэтоfalse.COALESCE(email, 'нет')подставляет значение вместо NULL.
Сортировка
ORDER BY price DESC, id сортирует по цене по убыванию, при равной цене по id. Без ORDER BY порядок строк не гарантирован, даже если «всегда приходило по порядку».
В PostgreSQL NULL при ASC идут в конце, при DESC в начале. Управлять: ORDER BY email NULLS LAST.
DISTINCT
SELECT DISTINCT city FROM customers убирает дубли по всему набору выбранных колонок. NULL считаются одинаковыми, поэтому в результате будет один NULL. В PostgreSQL есть DISTINCT ON (col): первая строка в каждой группе по ORDER BY.
Пагинация
LIMIT 20 OFFSET 40 пропускает 40 строк и отдаёт 20. Без ORDER BY страницы могут пересекаться. На больших OFFSET база всё равно читает и выбрасывает все пропущенные строки, поэтому для глубоких страниц используют keyset-пагинацию:
SELECT * FROM orders WHERE id > 1040 ORDER BY id LIMIT 20;
CASE
Условие внутри выражения:
SELECT title,
CASE WHEN price >= 1000 THEN 'дорого' ELSE 'нормально' END AS level
FROM products;
Частые ошибки
= NULLвместоIS NULL.- Псевдоним из SELECT в WHERE.
ORбез скобок рядом сAND.- Уверенность, что без ORDER BY порядок стабилен.
NOT INпо подзапросу, где может быть NULL.
Что спросят на собесе
- В каком порядке выполняются части SELECT и почему алиас нельзя в WHERE.
- Как работает NULL в сравнениях и чем
IS NULLотличается от= NULL. - Что вернёт
NOT INсо списком, где есть NULL. - Чем плох большой OFFSET и что вместо него.
- Гарантирован ли порядок строк без ORDER BY.