Как сортировать в 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, id

13. Сортировка по дате

-- последние сначала
ORDER BY created_at DESC

-- по дате без времени
ORDER BY DATE(created_at), created_at

14. Производительность 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+ вопросами для собесов.