Как сортировать в SQL: ORDER BY
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Зачем нужно уметь сортировать правильно
ORDER BY кажется простейшим: добавил в конец — и готово. На практике это один из самых частых источников багов в SQL-отчётах. Без ORDER BY результат недетерминированный — в разные запуски строки могут идти в разном порядке. LIMIT без ORDER BY — лотерея, которая сегодня возвращает нужные 10 строк, а завтра другие.
Плюс есть нюансы: где NULL — в начале или в конце (зависит от СУБД), как отсортировать кириллицу, как задать кастомный порядок категорий (pending → paid → refunded), как использовать индекс для ускорения. Senior-аналитик знает эти детали и не пишет «флаковые» запросы.
В статье — все частые паттерны:
- ASC / DESC по одной и нескольким колонкам
- NULLS FIRST / NULLS LAST в Postgres и workaround в MySQL
- Кастомная сортировка через CASE,
FIELD(),array_position - Case-insensitive и по кириллице через COLLATE
- Top-N и пагинация через ORDER BY + LIMIT / OFFSET
- Случайная выборка через RANDOM() / RAND() / TABLESAMPLE
- Индекс на sort-колонке — ускорение в 100×
Базовый синтаксис
SELECT * FROM users ORDER BY created_at;1. По возрастанию / убыванию
-- ASC (default)
SELECT * FROM users ORDER BY age;
SELECT * FROM users ORDER BY age ASC;
-- DESC
SELECT * FROM users ORDER BY age DESC;2. По нескольким колонкам
-- сначала по country, потом по age
SELECT * FROM users
ORDER BY country, age;
-- разные направления
SELECT * FROM users
ORDER BY country ASC, age DESC;3. По номеру колонки
-- сортировать по 2-й колонке SELECT
SELECT name, age, city FROM users
ORDER BY 2;
-- эквивалентно
ORDER BY age;На собесе обычно считается плохой стиль — читаемее с именем колонки.
4. По алиасу
SELECT
name,
age * 12 AS age_months
FROM users
ORDER BY age_months DESC;Можно сортировать по алиасу, определённому в SELECT.
5. По выражению
SELECT name, first_name, last_name
FROM users
ORDER BY LOWER(last_name);
-- или по длине строки
ORDER BY LENGTH(name);
-- или по конкатенации
ORDER BY first_name || ' ' || last_name;6. NULL в сортировке
По умолчанию:
- Postgres: NULL в конце при ASC
- MySQL: NULL в начале при ASC
Принудительно:
-- Postgres
ORDER BY age ASC NULLS LAST;
ORDER BY age ASC NULLS FIRST;
ORDER BY age DESC NULLS LAST;
-- MySQL (через IS NULL)
ORDER BY age IS NULL, age; -- NULL в конце
ORDER BY age IS NOT NULL, age; -- NULL в начале7. Кастомная сортировка
Через CASE
SELECT * FROM orders
ORDER BY CASE status
WHEN 'pending' THEN 1
WHEN 'paid' THEN 2
WHEN 'refunded' THEN 3
ELSE 4
END;Через массив позиций (Postgres)
SELECT * FROM orders
ORDER BY array_position(ARRAY['pending','paid','refunded'], status);Через FIELD (MySQL)
SELECT * FROM orders
ORDER BY FIELD(status, 'pending', 'paid', 'refunded');8. Сортировка по русскому алфавиту
По умолчанию сортировка использует locale сервера. Для точности:
-- Postgres
SELECT * FROM users ORDER BY name COLLATE "ru_RU";
-- MySQL
SELECT * FROM users ORDER BY name COLLATE utf8mb4_unicode_ci;9. Case-insensitive сортировка
ORDER BY LOWER(name);Или через COLLATE:
ORDER BY name COLLATE "C.UTF-8";10. Сортировка + LIMIT (топ-N)
-- топ-10 самых дорогих заказов
SELECT * FROM orders
ORDER BY total DESC
LIMIT 10;
-- пропустить первые 10, взять следующие 10 (пагинация)
SELECT * FROM orders
ORDER BY total DESC
LIMIT 10 OFFSET 10;11. Случайная сортировка
Postgres
SELECT * FROM users ORDER BY RANDOM() LIMIT 10;MySQL
SELECT * FROM users ORDER BY RAND() LIMIT 10;Медленно на больших таблицах. Для случайной выборки лучше TABLESAMPLE:
-- Postgres
SELECT * FROM users TABLESAMPLE BERNOULLI(1); -- 1% выборка12. Детерминистическая сортировка
Если ORDER BY не уникален → порядок недетерминистический:
ORDER BY salary
-- два сотрудника с salary=100 могут быть в любом порядкеДобавьте tie-breaker:
ORDER BY salary, id13. Сортировка по дате
-- последние сначала
ORDER BY created_at DESC
-- по дате без времени
ORDER BY DATE(created_at), created_at14. Производительность ORDER BY
Без индекса SQL сортирует все строки в памяти / на диске. Медленно для больших таблиц.
Индекс ускоряет:
CREATE INDEX idx_orders_created ON orders(created_at);
-- теперь быстро
SELECT * FROM orders ORDER BY created_at DESC LIMIT 10;Composite index для sort + filter:
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);
-- работает
SELECT * FROM orders WHERE user_id = 42 ORDER BY created_at DESC;Частые ошибки
1. Ожидать детерминистический порядок без ORDER BY
SELECT * FROM orders LIMIT 10;
-- порядок может измениться2. Сортировка после LIMIT
-- неверно (логически)
SELECT * FROM orders LIMIT 10 ORDER BY created_at;
-- правильно
SELECT * FROM orders ORDER BY created_at LIMIT 10;3. Сложные выражения без индекса
-- не использует индекс
ORDER BY LOWER(name);
-- functional index
CREATE INDEX idx_name_lower ON users(LOWER(name));4. NULL в неожиданном месте
В Postgres ASC NULL в конце. В MySQL — в начале. Всегда проверяйте.
5. LIMIT без ORDER BY
Результат недетерминирован. Всегда пара ORDER BY + LIMIT.
Связанные темы
FAQ
ORDER BY до или после GROUP BY?
После. Порядок: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT.
Сортировка нестабильная?
Может быть. Для стабильности добавьте tie-breaker (уникальная колонка).
NULL где?
В Postgres ASC — в конце. MySQL — в начале. Используйте NULLS FIRST/LAST.
Как быстро отсортировать миллион строк?
Индекс на sort-колонке. И LIMIT — если не нужно всё.
Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.