Чтобы отсортировать результат запроса по двум столбцам в разных направлениях, перечислите их через запятую и поставьте ASC или DESC после каждого: ORDER BY department ASC, salary DESC. База сначала выстроит строки по отделу от А до Я, а внутри каждого отдела поставит сотрудников от большей зарплаты к меньшей.
Направление относится только к тому столбцу, за которым стоит. Это самая частая ошибка новичков, поэтому ниже разберём её отдельно, а заодно посмотрим, куда при сортировке попадают пустые значения NULL. Примеры выполнены в PostgreSQL 17, MySQL 8.0 и SQLite 3.51. Если вы только знакомитесь с запросами, начните с разбора команды SELECT в MySQL.
SkillboxНейросети для рабочих задач31 290 ₽1 месяц
ЭдюсонSQL-разработчик: тариф PRO89 900 ₽2 месяца
ProductStarData-Driven подход в разработке продукта: SQL и Yandex DataLens29 990 ₽1 месяц
SkyproSQL с нуля134 640 ₽12 месяцев
SF EducationОбработка и анализ данных в SQL11 550 ₽1 месяц
НетологияSQL и получение данных37 300 ₽9 недель
Как работает ORDER BY: синтаксис, ASC и DESC

ORDER BY пишут в конце запроса, после WHERE, GROUP BY и HAVING. Дальше может идти только LIMIT с OFFSET. Как группировать строки и считать их по группам, мы разбирали в статье про функцию COUNT.
SELECT столбцы
FROM таблица
WHERE условие
ORDER BY столбец_1 [ASC | DESC], столбец_2 [ASC | DESC];
Два слова, которые нужно запомнить:
- ASC (ascending), по возрастанию: от меньшего числа к большему, от ранней даты к поздней, от А до Я;
- DESC (descending), по убыванию, в обратную сторону.
Если направление не указано, база сортирует по возрастанию. Так прямо написано в документации SQLite и в справке Microsoft по SQL Server: «Порядок сортировки по умолчанию — ASC». Поэтому ORDER BY salary и ORDER BY salary ASC дают одно и то же.
Дальше работаем с маленькой таблицей сотрудников. Восемь строк, у Жанны зарплата не заполнена:
| id | name | department | salary | hired |
|---|---|---|---|---|
| 1 | Анна | Продажи | 90000 | 2023-03-01 |
| 2 | Борис | Разработка | 180000 | 2021-07-15 |
| 3 | Вера | Продажи | 120000 | 2022-01-10 |
| 4 | Глеб | Разработка | 150000 | 2024-02-05 |
| 5 | Дина | Маркетинг | 110000 | 2022-09-20 |
| 6 | Егор | Разработка | 180000 | 2020-11-30 |
| 7 | Жанна | Маркетинг | NULL | 2025-01-13 |
| 8 | Зоя | Продажи | 90000 | 2024-06-03 |
Сортировка по убыванию зарплаты в MySQL:
SELECT name, salary FROM employees ORDER BY salary DESC;
Борис 180000
Егор 180000
Глеб 150000
Вера 120000
Дина 110000
Анна 90000
Зоя 90000
Жанна NULL
Сортировка по двум столбцам: ASC и DESC в одном запросе

Второй столбец в ORDER BY работает как запасной ключ. База смотрит на него только там, где значения первого столбца совпали.
- Строки делятся на группы по первому столбцу и выстраиваются в его порядке.
- Внутри каждой группы с одинаковым значением база сортирует по второму столбцу.
- Если совпали оба, в ход идёт третий, и так далее.
Запрос «отделы по алфавиту, внутри отдела сначала самые высокие зарплаты»:
SELECT name, department, salary
FROM employees
ORDER BY department ASC, salary DESC;
Дина Маркетинг 110000
Жанна Маркетинг NULL
Вера Продажи 120000
Анна Продажи 90000
Зоя Продажи 90000
Борис Разработка 180000
Егор Разработка 180000
Глеб Разработка 150000
Это вывод MySQL. Отдел идёт по возрастанию, а зарплата внутри отдела по убыванию. Столбцов можно перечислить сколько угодно, у каждого своё направление.
Похожий пример с датами: при равной зарплате выше поставить того, кто дольше работает в компании. Для этого добавляем hired ASC вторым ключом:
SELECT name, salary, hired
FROM employees
ORDER BY salary DESC, hired ASC;
Егор 180000 2020-11-30
Борис 180000 2021-07-15
Глеб 150000 2024-02-05
...
Канал основателя Checkroi Вани Буявца3 800 человек читают мой Телеграм-канал про нейросетиСобрал промпты для Claude Code и ChatGPT, разборы ИИ-инструментов и лайфхаки по продвижению бизнеса в одном местеПрисоединитьсяГлавная ошибка: DESC действует только на один столбец

Частая ситуация: человек пишет ORDER BY department, salary DESC и ждёт, что по убыванию отсортируются оба столбца. На деле DESC относится только к salary, а department сортируется по умолчанию, то есть по возрастанию.
В документации PostgreSQL этот случай разобран прямо: ORDER BY x, y DESC означает ORDER BY x ASC, y DESC, и это не то же самое, что ORDER BY x DESC, y DESC.
| Что написали | Что получили |
|---|---|
ORDER BY department, salary DESC |
Маркетинг → Продажи → Разработка, зарплата внутри отдела по убыванию |
ORDER BY department DESC, salary DESC |
Разработка → Продажи → Маркетинг, зарплата внутри отдела по убыванию |
ORDER BY department DESC, salary |
Разработка → Продажи → Маркетинг, зарплата внутри отдела по возрастанию |
Решение: ставьте ASC или DESC явно после каждого столбца, даже там, где хватило бы умолчания. Запрос станет длиннее на пару слов, зато через месяц вы прочитаете его без догадок.
Проверять такие запросы удобнее в графическом клиенте, где результат видно таблицей. Как подключить бесплатный DBeaver к базе, мы расписали по шагам на примере PostgreSQL.
Где окажутся NULL при сортировке
NULL означает, что значение не заполнено, с нулём и пустой строкой его путать нельзя (подробнее в статье про NULL в SQL). Такие строки разные СУБД ставят по-разному. Тот же запрос с DESC в PostgreSQL выводит NULL первой строкой, а в MySQL последней. Вот вывод PostgreSQL для ORDER BY salary DESC:
name | salary
-------+--------
Жанна |
Борис | 180000
Егор | 180000
...
Сводка по четырём СУБД, ссылки на документацию под таблицей:
| СУБД | NULL при ASC | NULL при DESC | NULLS FIRST / NULLS LAST |
|---|---|---|---|
| PostgreSQL | в конце | в начале | есть |
| MySQL | в начале | в конце | нет, синтаксическая ошибка |
| SQLite | в начале | в конце | есть |
| SQL Server | в начале | в конце | нет в синтаксисе ORDER BY |
PostgreSQL считает NULL больше любого значения, поэтому NULLS FIRST там по умолчанию для DESC (документация PostgreSQL). SQLite, наоборот, считает NULL меньше любого значения (документация SQLite). MySQL выводит NULL первыми при ASC и последними при DESC (справка MySQL), у SQL Server NULL тоже наименьшие (справка Microsoft).
Почему это важно: запрос «двое сотрудников с самой высокой зарплатой» в PostgreSQL вернёт Жанну, у которой зарплаты нет вовсе.
SELECT name FROM employees ORDER BY salary DESC LIMIT 2;
-- PostgreSQL: Жанна, Борис
SELECT name FROM employees ORDER BY salary DESC NULLS LAST LIMIT 2;
-- PostgreSQL: Борис, Егор
В MySQL на NULLS LAST будет ошибка 1064. Там NULL отправляют в конец отдельным ключом сортировки. Вариант с CASE мы выполнили в MySQL, PostgreSQL и SQLite, а в справке Microsoft сортировка через CASE разобрана на примерах:
SELECT name, salary
FROM employees
ORDER BY CASE WHEN salary IS NULL THEN 1 ELSE 0 END, salary DESC;
Строки с NULL получают 1, остальные 0, и пустые зарплаты уходят вниз при любом направлении. В MySQL и PostgreSQL то же самое можно записать короче: ORDER BY salary IS NULL, salary DESC. Если NULL нужно не убрать вниз, а заменить на ноль или текст, поможет функция COALESCE.
Сортировка по выражению, псевдониму и номеру столбца
После ORDER BY можно ставить не только имя столбца из таблицы.
Псевдоним из SELECT
SELECT name, salary * 12 AS year_pay
FROM employees
ORDER BY year_pay DESC;
Псевдоним работает везде, если стоит в ORDER BY один. С выражением сложнее: ORDER BY year_pay + 1 MySQL выполнит, а PostgreSQL ответит ошибкой «столбец year_pay не существует». В документации PostgreSQL это ограничение описано отдельно: имя выходного столбца нельзя использовать внутри выражения, только само по себе.
Номер столбца
ORDER BY 2, 3 DESC сортирует по второму и третьему столбцу из списка SELECT. В запросе SELECT name, department, salary это отдел и зарплата.
Так удобно писать в консоли, но в коде лучше имена: стоит кому-то добавить столбец в начало SELECT, и сортировка без всякой ошибки пойдёт по другому полю.
Свой порядок через CASE
Когда алфавит не подходит, порядок задают вручную. Например, сначала разработка, потом продажи, остальные отделы в конце:
SELECT name, department
FROM employees
ORDER BY CASE department
WHEN 'Разработка' THEN 1
WHEN 'Продажи' THEN 2
ELSE 3
END,
name;
Борис Разработка
Глеб Разработка
Егор Разработка
Анна Продажи
Вера Продажи
Зоя Продажи
Дина Маркетинг
Жанна Маркетинг
Больше приёмов с этим оператором собрано в разборе CASE в SQL на примерах.
Частые ошибки и что с ними делать
Одинаковые значения меняют порядок от запуска к запуску
Борис и Егор получают одинаково. В наших прогонах первым во всех трёх СУБД шёл Борис, но это совпадение: после изменения данных или при другом плане запроса первым может оказаться Егор. И такой результат тоже будет верным: если значения совпали, порядок между такими строками ничем не задан.
Решение: добавляйте последним ключом уникальный столбец, обычно id. Особенно это важно для запросов с LIMIT. Документация PostgreSQL про LIMIT предупреждает: если ORDER BY не задаёт строкам однозначный порядок, вы получите непредсказуемый набор строк.
SELECT name, salary FROM employees ORDER BY salary DESC, id LIMIT 3;
Числа в текстовом столбце сортируются как строки
Если номера заказов или артикулы хранятся в VARCHAR, база сравнивает их посимвольно. В наших прогонах все три СУБД выдали одно и то же: 10, 100, 25, 9.
Решение: приведите значение к числу прямо в ORDER BY. В PostgreSQL и SQLite подходит CAST(v AS INT). MySQL такую запись не принимает, ему нужен CAST(v AS SIGNED) или UNSIGNED (справка MySQL по CAST). После приведения порядок правильный: 9, 10, 25, 100.
DISTINCT и сортировка по столбцу не из SELECT
Обычно сортировать можно и по полю, которого нет в выводе: SELECT name FROM employees ORDER BY hired DESC работает везде. С DISTINCT так нельзя. PostgreSQL и MySQL отвечают ошибкой, потому что одному уникальному отделу соответствует несколько зарплат и непонятно, по какой из них ставить строку. SQLite запрос выполнит без ошибки, но по какой из зарплат он поставил каждый отдел, из запроса не понять.
Решение: добавьте этот столбец в SELECT или сгруппируйте и сортируйте по агрегату, например ORDER BY MAX(salary). Как работают уникальные значения, объясняем в статье про DISTINCT.
Медленная сортировка на большой таблице
Для частого запроса с ORDER BY и LIMIT помогает индекс по тем же столбцам в том же порядке. Индекс (department, salary) закрывает и ORDER BY department, salary, и обратный порядок по обоим столбцам. А вот для смешанного department ASC, salary DESC PostgreSQL предлагает отдельный индекс (department ASC, salary DESC) (раздел документации про индексы и ORDER BY).
Мы проверили это на таблице в 200 тысяч строк. С обычным индексом PostgreSQL 17 досортировывал зарплаты отдельным шагом Incremental Sort. Со смешанным индексом план свёлся к одному чтению индекса.
Где освоить SQL системно
ORDER BY осваивают в SQL одним из первых. Дальше идут соединение таблиц через JOIN, группировка и оконные функции, где ORDER BY появляется ещё раз внутри OVER. Всё это последовательно разбирают на курсах. Ниже подборка из каталога Checkroi, а если вы присматриваетесь к профессии, посмотрите план, как стать SQL-разработчиком.
| Курс | Школа | Стоимость со скидкой | В рассрочку | Длительность | Обзор курса от Checkroi |
|---|---|---|---|---|---|
| Нейросети для рабочих задач Перейти на сайт курса | 31 290 ₽ | - | 1 месяц | Обзор курса | |
| SQL для анализа данных Перейти на сайт курса | 50 865 ₽ | 6203 ₽/мес. | 2 месяца | Обзор курса | |
| SQL-разработчик: тариф PRO Перейти на сайт курса | 89 900 ₽ | 7491 ₽/мес. | 2 месяца | Обзор курса | |
| SQL-разработчик: тариф Базовый Перейти на сайт курса | 79 900 ₽ | 3329 ₽/мес. | 3 месяца | Обзор курса | |
| Data-Driven подход в разработке продукта: SQL и Yandex DataLens Перейти на сайт курса | 29 990 ₽ | 2999 ₽/мес. | 1 месяц | Обзор курса |
Больше программ — в полном каталоге курсов по SQL




