ORDER BY — это часть SQL-запроса, которая отвечает за сортировку результата.
Когда ты пишешь запрос без ORDER BY, база данных не обязана возвращать строки в каком-то понятном порядке. Сегодня они могут прийти так, завтра иначе, после добавления индекса — вообще по-другому. Для базы это нормально: она выбирает удобный для себя способ достать данные. Но для человека такой результат выглядит как хаос.
ORDER BY решает эту проблему. С его помощью ты явно говоришь базе:
Отсортируй результат вот по этой колонке: от меньшего к большему или от большего к меньшему.
Представь стопку книг, которую высыпали на стол как попало. Если попросить разложить книги по названию, это будет похоже на ORDER BY title. Если попросить сначала положить самые новые книги, это уже ORDER BY year DESC.
Зачем нужен ORDER BY
Сортировка нужна почти в любом реальном приложении.
Без неё неудобно смотреть списки, строить рейтинги, выводить историю действий, показывать товары, новости, заказы и отчёты.
Типичные ситуации:
- показать товары от дешёвых к дорогим;
- вывести последние новости сверху;
- собрать топ пользователей по очкам;
- отсортировать города по алфавиту;
- показать заказы от новых к старым;
- найти самые крупные платежи;
- построить лидерборд.
Например, запрос без сортировки:
SELECT id, title, price
FROM products;
Может вернуть товары в любом порядке. Не потому что база ошиблась, а потому что ты не сказал ей, какой порядок нужен.
А вот так уже понятно:
SELECT id, title, price
FROM products
ORDER BY price;
Теперь товары будут отсортированы по цене от меньшей к большей.
Базовый синтаксис ORDER BY
Общий вид такой:
SELECT column1, column2
FROM table_name
ORDER BY column_name ASC;
Или так:
SELECT column1, column2
FROM table_name
ORDER BY column_name DESC;
Есть два направления сортировки:
ASC — по возрастанию;
DESC — по убыванию.
ASC используется по умолчанию. Поэтому эти два запроса означают одно и то же:
SELECT title, price
FROM products
ORDER BY price ASC;
SELECT title, price
FROM products
ORDER BY price;
Оба запроса отсортируют товары от самой маленькой цены к самой большой.
А если нужно наоборот — от дорогих к дешёвым, используй DESC:
SELECT title, price
FROM products
ORDER BY price DESC;
Пример с книгами
Допустим, есть таблица books:
| id |
title |
author |
year |
rating |
| 1 |
War and Peace |
Leo Tolstoy |
1869 |
4.8 |
| 2 |
The Master and Margarita |
Mikhail Bulgakov |
1967 |
4.9 |
| 3 |
And Quiet Flows the Don |
Mikhail Sholokhov |
1940 |
4.7 |
| 4 |
Crime and Punishment |
Fyodor Dostoevsky |
1866 |
4.8 |
Отсортируем книги по году издания от старых к новым:
SELECT title, year
FROM books
ORDER BY year;
Результат:
| title |
year |
| Crime and Punishment |
1866 |
| War and Peace |
1869 |
| And Quiet Flows the Don |
1940 |
| The Master and Margarita |
1967 |
Поскольку мы не написали DESC, база использовала ASC — сортировку по возрастанию.
Теперь сделаем наоборот: сначала новые книги, потом старые.
SELECT title, year
FROM books
ORDER BY year DESC;
Результат:
| title |
year |
| The Master and Margarita |
1967 |
| And Quiet Flows the Don |
1940 |
| War and Peace |
1869 |
| Crime and Punishment |
1866 |
DESC перевернул порядок.
ASC и DESC на разных типах данных
ORDER BY работает не только с числами.
Числа сортируются по величине:
SELECT title, price
FROM products
ORDER BY price;
Даты сортируются по времени:
SELECT title, created_at
FROM posts
ORDER BY created_at DESC;
Строки сортируются по алфавиту:
SELECT name
FROM users
ORDER BY name;
Для новичка важно запомнить простую логику:
| Тип данных |
ASC |
DESC |
| Числа |
от меньшего к большему |
от большего к меньшему |
| Даты |
от старых к новым |
от новых к старым |
| Строки |
по алфавиту |
в обратном алфавитном порядке |
В интерфейсах часто используют именно DESC по дате, потому что пользователю обычно интересны свежие записи.
Например:
SELECT title, published_at
FROM articles
ORDER BY published_at DESC;
Так мы получим сначала последние статьи, а потом более старые.
ORDER BY по нескольким колонкам
Иногда одной колонки для сортировки недостаточно.
Например, мы хотим отсортировать сотрудников сначала по отделу, а внутри каждого отдела — по зарплате от большей к меньшей.
Для этого в ORDER BY можно перечислить несколько колонок через запятую:
SELECT name, department, salary
FROM employees
ORDER BY department ASC, salary DESC;
База будет сортировать так:
- Сначала по
department ASC.
- Если отдел одинаковый — внутри отдела по
salary DESC.
- Если и зарплата одинаковая — порядок между такими строками всё ещё может быть произвольным.
Допустим, есть таблица employees:
| id |
name |
department |
salary |
| 1 |
Anna |
IT |
120000 |
| 2 |
Boris |
Sales |
80000 |
| 3 |
Vera |
IT |
150000 |
| 4 |
Gregory |
HR |
70000 |
| 5 |
Denis |
IT |
120000 |
| 6 |
Elena |
Sales |
95000 |
Запрос:
SELECT name, department, salary
FROM employees
ORDER BY department ASC, salary DESC;
Результат:
| name |
department |
salary |
| Gregory |
HR |
70000 |
| Vera |
IT |
150000 |
| Anna |
IT |
120000 |
| Denis |
IT |
120000 |
| Elena |
Sales |
95000 |
| Boris |
Sales |
80000 |
Сначала идут отделы по алфавиту: HR, IT, Sales. А внутри каждого отдела сотрудники отсортированы по зарплате от большей к меньшей.
Но обрати внимание: у Anna и Denis одинаковый отдел и одинаковая зарплата. Значит, между ними порядок не гарантирован. Сегодня Anna может быть выше Denis, завтра наоборот.
Если нужен стабильный и предсказуемый порядок, добавь ещё одну колонку:
SELECT name, department, salary
FROM employees
ORDER BY department ASC, salary DESC, name ASC;
Теперь при одинаковой зарплате сотрудники будут отсортированы по имени.
Почему важна стабильная сортировка
В учебных примерах часто кажется: «Ну какая разница, в каком порядке идут две одинаковые строки?»
В реальных задачах разница есть.
Представь страницу с заказами. На первой странице пользователь видит 20 заказов. Потом нажимает «следующая страница». Если сортировка нестабильная, некоторые заказы могут повториться, а некоторые — пропасть между страницами.
Особенно это важно при связке ORDER BY и LIMIT.
Плохо:
SELECT id, customer_id, created_at
FROM orders
ORDER BY created_at DESC
LIMIT 20;
Если у нескольких заказов одинаковый created_at, база может менять их внутренний порядок.
Надёжнее:
SELECT id, customer_id, created_at
FROM orders
ORDER BY created_at DESC, id DESC
LIMIT 20;
Здесь id добавлен как дополнительный критерий. Он делает порядок детерминированным: если даты одинаковые, выше будет заказ с большим id.
Хорошая привычка: если сортируешь по колонке, где могут быть одинаковые значения, добавляй уникальный ключ в конец сортировки.
ORDER BY и LIMIT
Одна из самых популярных комбинаций в SQL — ORDER BY вместе с LIMIT.
ORDER BY сначала расставляет строки в нужном порядке, а LIMIT берёт только первые.
Например, нужно получить топ-3 поста по лайкам:
SELECT title, likes
FROM posts
ORDER BY likes DESC
LIMIT 3;
Логика такая:
ORDER BY likes DESC ставит самые популярные посты сверху.
LIMIT 3 оставляет только первые три строки.
Без ORDER BY запрос был бы ошибочным по смыслу:
SELECT title, likes
FROM posts
LIMIT 3;
Такой запрос вернёт просто три строки. Не самые новые, не самые популярные, не самые важные. Просто какие-то три строки, которые базе оказалось удобно вернуть первыми.
Для топов, рейтингов, последних записей и «самых-самых» ORDER BY обязателен.
Пример: последние заказы
Допустим, нужно показать 5 последних заказов.
SELECT id, customer_id, amount, created_at
FROM orders
ORDER BY created_at DESC, id DESC
LIMIT 5;
Почему здесь два поля в сортировке?
created_at DESC ставит свежие заказы выше старых.
id DESC помогает, если несколько заказов созданы в одну и ту же секунду.
Такой запрос подходит для реального интерфейса: порядок будет понятным и стабильным.
Сортировка по выражению
В ORDER BY можно указывать не только готовые колонки, но и выражения.
Например, есть таблица posts с лайками и просмотрами:
| id |
title |
likes |
views |
| 1 |
SQL Basics |
50 |
1000 |
| 2 |
Window Functions |
80 |
400 |
| 3 |
Joins Explained |
120 |
3000 |
Можно отсортировать посты не просто по лайкам, а по доле лайков от просмотров:
SELECT title, likes, views
FROM posts
ORDER BY likes * 1.0 / views DESC;
В таблице нет отдельной колонки с таким показателем. База посчитает выражение на лету и отсортирует результат по нему.
Здесь 1.0 помогает получить дробное деление, а не целочисленное.
Ещё пример — сортировка по длине имени:
SELECT name
FROM users
ORDER BY LENGTH(name) DESC;
Сначала будут пользователи с самыми длинными именами.
Сортировка по вычисляемой колонке
Часто выражение удобно вывести в SELECT, дать ему понятное имя и потом сортировать по этому имени.
Например:
SELECT
title,
likes,
views,
likes * 1.0 / views AS engagement_rate
FROM posts
ORDER BY engagement_rate DESC;
Так запрос читается легче:
- в
SELECT мы считаем показатель;
- в
ORDER BY сортируем по нему.
Это особенно удобно в отчётах, где есть расчётные метрики: конверсия, средний чек, CTR, доля от общего числа и так далее.
Сортировка через CASE
Иногда обычная сортировка по алфавиту не подходит.
Например, есть статусы заказов:
- paid;
- pending;
- cancelled.
Если отсортировать по статусу как по строке, база расставит их по алфавиту. Но бизнесу может быть нужен другой порядок: сначала оплаченные, потом ожидающие, потом отменённые.
Для такого случая используют CASE в ORDER BY.
Допустим, есть таблица orders:
| id |
customer |
amount |
status |
created_at |
| 1 |
Anna |
500 |
paid |
2024-03-10 |
| 2 |
Boris |
1500 |
paid |
2024-03-15 |
| 3 |
Vera |
200 |
cancelled |
2024-03-12 |
| 4 |
Gregory |
3000 |
paid |
2024-03-08 |
| 5 |
Denis |
800 |
pending |
2024-03-15 |
Нужно вывести заказы так:
- Сначала
paid.
- Потом
pending.
- Потом
cancelled.
- Внутри каждого статуса — новые заказы выше старых.
- Если даты одинаковые — большая сумма выше.
Запрос:
SELECT id, customer, status, amount, created_at
FROM orders
ORDER BY
CASE status
WHEN 'paid' THEN 1
WHEN 'pending' THEN 2
WHEN 'cancelled' THEN 3
ELSE 4
END,
created_at DESC,
amount DESC;
Результат:
| id |
customer |
status |
amount |
created_at |
| 2 |
Boris |
paid |
1500 |
2024-03-15 |
| 1 |
Anna |
paid |
500 |
2024-03-10 |
| 4 |
Gregory |
paid |
3000 |
2024-03-08 |
| 5 |
Denis |
pending |
800 |
2024-03-15 |
| 3 |
Vera |
cancelled |
200 |
2024-03-12 |
CASE здесь превращает статусы в условные числа для сортировки:
- paid получает 1;
- pending получает 2;
- cancelled получает 3;
- всё остальное получает 4.
Так мы сортируем не по алфавиту, а по смыслу.
NULL в сортировке
NULL — это отсутствие значения. И при сортировке с ним нужно быть внимательным.
Например, есть статьи, у части из них есть дата публикации, а часть пока в черновиках:
| id |
title |
published_at |
| 1 |
SQL SELECT |
2024-03-10 |
| 2 |
SQL JOIN |
NULL |
| 3 |
SQL ORDER BY |
2024-03-15 |
Если отсортировать по дате, возникает вопрос: куда положить строки с NULL? В начало или в конец?
Разные базы данных ведут себя по-разному.
В PostgreSQL:
- при
ASC значения NULL обычно идут в конце;
- при
DESC значения NULL обычно идут в начале.
В MySQL:
- при
ASC значения NULL обычно идут в начале;
- при
DESC значения NULL обычно идут в конце.
Если ты работаешь в PostgreSQL и хочешь явно управлять положением NULL, используй NULLS FIRST или NULLS LAST.
Например, показать сначала опубликованные статьи от свежих к старым, а черновики без даты оставить в конце:
SELECT title, published_at
FROM articles
ORDER BY published_at DESC NULLS LAST;
А если нужно наоборот — строки без даты поднять наверх:
SELECT title, published_at
FROM articles
ORDER BY published_at DESC NULLS FIRST;
В MySQL синтаксиса NULLS FIRST и NULLS LAST нет. Там часто используют выражение:
SELECT title, published_at
FROM articles
ORDER BY published_at IS NULL, published_at DESC;
published_at IS NULL вернёт условный признак: дата отсутствует или нет. Благодаря этому можно управлять тем, куда попадут строки с пустой датой.
ORDER BY после WHERE
ORDER BY выполняется после фильтрации.
Например, нужно взять только оплаченные заказы и отсортировать их от новых к старым:
SELECT id, customer_id, amount, created_at
FROM orders
WHERE status = 'paid'
ORDER BY created_at DESC;
Сначала WHERE оставит только оплаченные заказы. Потом ORDER BY отсортирует уже отфильтрованный результат.
Порядок частей запроса важен:
SELECT columns
FROM table_name
WHERE condition
ORDER BY column_name;
Нельзя поставить ORDER BY перед WHERE или FROM. SQL ожидает части запроса в своём порядке.
ORDER BY после GROUP BY
ORDER BY часто используют вместе с группировкой.
Например, нужно посчитать выручку по каждому пользователю и отсортировать клиентов по сумме покупок:
SELECT
customer_id,
SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
ORDER BY total_amount DESC;
Здесь происходит несколько шагов:
GROUP BY customer_id собирает заказы по клиентам.
SUM(amount) считает сумму заказов каждого клиента.
ORDER BY total_amount DESC ставит клиентов с самой большой суммой наверх.
Так получаются рейтинги, топы и отчёты.
Ещё пример: найти самые активные категории товаров.
SELECT
category_id,
COUNT(*) AS products_count
FROM products
GROUP BY category_id
ORDER BY products_count DESC;
Сначала считаем количество товаров в каждой категории, потом сортируем категории по этому количеству.
Можно ли сортировать по колонке, которой нет в SELECT
Обычно да.
Например:
SELECT title
FROM books
ORDER BY rating DESC;
В результате мы выводим только title, но сортируем по rating. Во многих базах данных это нормальная ситуация.
Это удобно, когда колонка нужна только для порядка, но не нужна пользователю в выдаче.
Однако есть важный нюанс с DISTINCT.
Например:
SELECT DISTINCT author
FROM books
ORDER BY rating DESC;
Такой запрос может быть проблемным. Почему?
DISTINCT оставляет уникальных авторов. Но у одного автора может быть несколько книг с разными рейтингами. База не всегда может понять, какой именно rating использовать для сортировки автора.
Поэтому с DISTINCT лучше сортировать по тем выражениям, которые есть в результате, или сначала аккуратно агрегировать данные.
Например:
SELECT
author,
MAX(rating) AS best_rating
FROM books
GROUP BY author
ORDER BY best_rating DESC;
Теперь всё честно: для каждого автора мы посчитали лучший рейтинг и сортируем именно по нему.
ORDER BY и UNION
Если ты объединяешь результаты через UNION, сортировку обычно пишут один раз в самом конце.
Например:
SELECT name, email
FROM customers
UNION
SELECT name, email
FROM partners
ORDER BY name;
Здесь ORDER BY name сортирует общий результат после объединения.
Не нужно ставить отдельный ORDER BY в каждую часть запроса, если тебе нужен итоговый порядок всей выдачи.
Правильная мысль такая: сначала собрать общий список, потом отсортировать его.
Числа, сохранённые как строки
Одна из неприятных ошибок новичков — хранить числа в текстовой колонке.
Представь, что цены лежат не в числовом типе, а в строковом:
| title |
price_text |
| A |
10 |
| B |
2 |
| C |
100 |
Запрос:
SELECT title, price_text
FROM products
ORDER BY price_text;
Может дать такой порядок:
| title |
price_text |
| A |
10 |
| C |
100 |
| B |
2 |
Для человека это странно: 2 должно быть раньше 10. Но для строки всё логично: база сравнивает текст посимвольно, а не как числа.
Чтобы отсортировать как числа, можно привести тип:
SELECT title, price_text
FROM products
ORDER BY CAST(price_text AS INTEGER);
Но лучше хранить числа в числовых колонках, а не в строках. Тогда сортировка, фильтрация и расчёты будут работать нормально.
ORDER BY по номеру колонки
SQL позволяет сортировать по номеру колонки из SELECT.
Например:
SELECT title, rating
FROM books
ORDER BY 2 DESC;
2 означает вторую колонку в результате, то есть rating.
Запрос сработает. Но для учебного и рабочего кода это не лучший стиль.
Почему?
Потому что такой запрос хуже читается. Нужно глазами считать, какая колонка в SELECT первая, какая вторая. А если кто-то поменяет порядок колонок, сортировка может случайно измениться.
Лучше писать явно:
SELECT title, rating
FROM books
ORDER BY rating DESC;
Так понятнее и тебе, и человеку, который будет читать запрос после тебя.
Частые ошибки с ORDER BY
Думать, что порядок есть сам по себе
Самая частая ошибка: считать, что база вернёт строки в порядке добавления.
Например:
SELECT id, title
FROM posts;
Может показаться, что посты будут идти по id. Но SQL этого не обещает.
Нужен порядок по id — напиши явно:
SELECT id, title
FROM posts
ORDER BY id;
Нужны свежие посты — тоже явно:
SELECT id, title, created_at
FROM posts
ORDER BY created_at DESC, id DESC;
Использовать LIMIT без ORDER BY
Плохо:
SELECT id, title
FROM posts
LIMIT 10;
Так ты получишь просто 10 строк. Не топ, не последние, не первые по смыслу.
Хорошо:
SELECT id, title, created_at
FROM posts
ORDER BY created_at DESC, id DESC
LIMIT 10;
Теперь это действительно 10 последних постов.
Забыть направление сортировки
Если написать просто ORDER BY created_at, база отсортирует по возрастанию. То есть сначала будут самые старые даты.
Для ленты новостей обычно нужно наоборот:
SELECT title, created_at
FROM posts
ORDER BY created_at DESC;
Не добавить второй критерий при одинаковых значениях
Плохо:
SELECT id, title, rating
FROM books
ORDER BY rating DESC;
Если у нескольких книг одинаковый рейтинг, порядок между ними не гарантирован.
Лучше:
SELECT id, title, rating
FROM books
ORDER BY rating DESC, id ASC;
Теперь сортировка стабильнее.
Сортировать текстовые числа как настоящие числа
Если колонка строковая, сортировка будет строковой:
SELECT title, price_text
FROM products
ORDER BY price_text;
Если данные уже такие, можно привести тип:
SELECT title, price_text
FROM products
ORDER BY CAST(price_text AS INTEGER);
Но правильнее хранить цену в числовой колонке.
Главное
ORDER BY нужен, чтобы задать понятный и предсказуемый порядок строк в результате запроса.
Без него база не гарантирует порядок. Даже если тебе кажется, что строки «и так идут нормально», это случайность, а не правило.
Главные шаблоны:
SELECT title, price
FROM products
ORDER BY price;
SELECT title, price
FROM products
ORDER BY price DESC;
SELECT name, department, salary
FROM employees
ORDER BY department ASC, salary DESC;
SELECT title, created_at
FROM posts
ORDER BY created_at DESC, id DESC
LIMIT 10;
Запомни:
ASC — по возрастанию;
DESC — по убыванию;
ASC можно не писать, он используется по умолчанию;
- несколько колонок в
ORDER BY дают многоуровневую сортировку;
ORDER BY вместе с LIMIT позволяет делать топы и последние записи;
- для стабильного порядка добавляй уникальную колонку, например
id;
- с
NULL и строковыми числами нужно быть особенно внимательным.
ORDER BY — это не украшение запроса, а способ превратить набор строк в понятный список. Именно с него начинается нормальная выдача: товары становятся отсортированными, отчёты — читаемыми, ленты — свежими, а топы — честными.
ORDER BY— это часть SQL-запроса, которая отвечает за сортировку результата.Когда ты пишешь запрос без
ORDER BY, база данных не обязана возвращать строки в каком-то понятном порядке. Сегодня они могут прийти так, завтра иначе, после добавления индекса — вообще по-другому. Для базы это нормально: она выбирает удобный для себя способ достать данные. Но для человека такой результат выглядит как хаос.ORDER BYрешает эту проблему. С его помощью ты явно говоришь базе:Представь стопку книг, которую высыпали на стол как попало. Если попросить разложить книги по названию, это будет похоже на
ORDER BY title. Если попросить сначала положить самые новые книги, это ужеORDER BY year DESC.Зачем нужен ORDER BY
Сортировка нужна почти в любом реальном приложении.
Без неё неудобно смотреть списки, строить рейтинги, выводить историю действий, показывать товары, новости, заказы и отчёты.
Типичные ситуации:
Например, запрос без сортировки:
SELECT id, title, price FROM products;Может вернуть товары в любом порядке. Не потому что база ошиблась, а потому что ты не сказал ей, какой порядок нужен.
А вот так уже понятно:
SELECT id, title, price FROM products ORDER BY price;Теперь товары будут отсортированы по цене от меньшей к большей.
Базовый синтаксис ORDER BY
Общий вид такой:
SELECT column1, column2 FROM table_name ORDER BY column_name ASC;Или так:
SELECT column1, column2 FROM table_name ORDER BY column_name DESC;Есть два направления сортировки:
ASC— по возрастанию;DESC— по убыванию.ASCиспользуется по умолчанию. Поэтому эти два запроса означают одно и то же:SELECT title, price FROM products ORDER BY price ASC;SELECT title, price FROM products ORDER BY price;Оба запроса отсортируют товары от самой маленькой цены к самой большой.
А если нужно наоборот — от дорогих к дешёвым, используй
DESC:SELECT title, price FROM products ORDER BY price DESC;Пример с книгами
Допустим, есть таблица
books:Отсортируем книги по году издания от старых к новым:
SELECT title, year FROM books ORDER BY year;Результат:
Поскольку мы не написали
DESC, база использовалаASC— сортировку по возрастанию.Теперь сделаем наоборот: сначала новые книги, потом старые.
SELECT title, year FROM books ORDER BY year DESC;Результат:
DESCперевернул порядок.ASC и DESC на разных типах данных
ORDER BYработает не только с числами.Числа сортируются по величине:
SELECT title, price FROM products ORDER BY price;Даты сортируются по времени:
SELECT title, created_at FROM posts ORDER BY created_at DESC;Строки сортируются по алфавиту:
SELECT name FROM users ORDER BY name;Для новичка важно запомнить простую логику:
ASCDESCВ интерфейсах часто используют именно
DESCпо дате, потому что пользователю обычно интересны свежие записи.Например:
SELECT title, published_at FROM articles ORDER BY published_at DESC;Так мы получим сначала последние статьи, а потом более старые.
ORDER BY по нескольким колонкам
Иногда одной колонки для сортировки недостаточно.
Например, мы хотим отсортировать сотрудников сначала по отделу, а внутри каждого отдела — по зарплате от большей к меньшей.
Для этого в
ORDER BYможно перечислить несколько колонок через запятую:SELECT name, department, salary FROM employees ORDER BY department ASC, salary DESC;База будет сортировать так:
department ASC.salary DESC.Допустим, есть таблица
employees:Запрос:
SELECT name, department, salary FROM employees ORDER BY department ASC, salary DESC;Результат:
Сначала идут отделы по алфавиту: HR, IT, Sales. А внутри каждого отдела сотрудники отсортированы по зарплате от большей к меньшей.
Но обрати внимание: у Anna и Denis одинаковый отдел и одинаковая зарплата. Значит, между ними порядок не гарантирован. Сегодня Anna может быть выше Denis, завтра наоборот.
Если нужен стабильный и предсказуемый порядок, добавь ещё одну колонку:
SELECT name, department, salary FROM employees ORDER BY department ASC, salary DESC, name ASC;Теперь при одинаковой зарплате сотрудники будут отсортированы по имени.
Почему важна стабильная сортировка
В учебных примерах часто кажется: «Ну какая разница, в каком порядке идут две одинаковые строки?»
В реальных задачах разница есть.
Представь страницу с заказами. На первой странице пользователь видит 20 заказов. Потом нажимает «следующая страница». Если сортировка нестабильная, некоторые заказы могут повториться, а некоторые — пропасть между страницами.
Особенно это важно при связке
ORDER BYиLIMIT.Плохо:
SELECT id, customer_id, created_at FROM orders ORDER BY created_at DESC LIMIT 20;Если у нескольких заказов одинаковый
created_at, база может менять их внутренний порядок.Надёжнее:
SELECT id, customer_id, created_at FROM orders ORDER BY created_at DESC, id DESC LIMIT 20;Здесь
idдобавлен как дополнительный критерий. Он делает порядок детерминированным: если даты одинаковые, выше будет заказ с большимid.Хорошая привычка: если сортируешь по колонке, где могут быть одинаковые значения, добавляй уникальный ключ в конец сортировки.
ORDER BY и LIMIT
Одна из самых популярных комбинаций в SQL —
ORDER BYвместе сLIMIT.ORDER BYсначала расставляет строки в нужном порядке, аLIMITберёт только первые.Например, нужно получить топ-3 поста по лайкам:
SELECT title, likes FROM posts ORDER BY likes DESC LIMIT 3;Логика такая:
ORDER BY likes DESCставит самые популярные посты сверху.LIMIT 3оставляет только первые три строки.Без
ORDER BYзапрос был бы ошибочным по смыслу:SELECT title, likes FROM posts LIMIT 3;Такой запрос вернёт просто три строки. Не самые новые, не самые популярные, не самые важные. Просто какие-то три строки, которые базе оказалось удобно вернуть первыми.
Для топов, рейтингов, последних записей и «самых-самых»
ORDER BYобязателен.Пример: последние заказы
Допустим, нужно показать 5 последних заказов.
SELECT id, customer_id, amount, created_at FROM orders ORDER BY created_at DESC, id DESC LIMIT 5;Почему здесь два поля в сортировке?
created_at DESCставит свежие заказы выше старых.id DESCпомогает, если несколько заказов созданы в одну и ту же секунду.Такой запрос подходит для реального интерфейса: порядок будет понятным и стабильным.
Сортировка по выражению
В
ORDER BYможно указывать не только готовые колонки, но и выражения.Например, есть таблица
postsс лайками и просмотрами:Можно отсортировать посты не просто по лайкам, а по доле лайков от просмотров:
SELECT title, likes, views FROM posts ORDER BY likes * 1.0 / views DESC;В таблице нет отдельной колонки с таким показателем. База посчитает выражение на лету и отсортирует результат по нему.
Здесь
1.0помогает получить дробное деление, а не целочисленное.Ещё пример — сортировка по длине имени:
SELECT name FROM users ORDER BY LENGTH(name) DESC;Сначала будут пользователи с самыми длинными именами.
Сортировка по вычисляемой колонке
Часто выражение удобно вывести в
SELECT, дать ему понятное имя и потом сортировать по этому имени.Например:
SELECT title, likes, views, likes * 1.0 / views AS engagement_rate FROM posts ORDER BY engagement_rate DESC;Так запрос читается легче:
SELECTмы считаем показатель;ORDER BYсортируем по нему.Это особенно удобно в отчётах, где есть расчётные метрики: конверсия, средний чек, CTR, доля от общего числа и так далее.
Сортировка через CASE
Иногда обычная сортировка по алфавиту не подходит.
Например, есть статусы заказов:
Если отсортировать по статусу как по строке, база расставит их по алфавиту. Но бизнесу может быть нужен другой порядок: сначала оплаченные, потом ожидающие, потом отменённые.
Для такого случая используют
CASEвORDER BY.Допустим, есть таблица
orders:Нужно вывести заказы так:
paid.pending.cancelled.Запрос:
SELECT id, customer, status, amount, created_at FROM orders ORDER BY CASE status WHEN 'paid' THEN 1 WHEN 'pending' THEN 2 WHEN 'cancelled' THEN 3 ELSE 4 END, created_at DESC, amount DESC;Результат:
CASEздесь превращает статусы в условные числа для сортировки:Так мы сортируем не по алфавиту, а по смыслу.
NULL в сортировке
NULL— это отсутствие значения. И при сортировке с ним нужно быть внимательным.Например, есть статьи, у части из них есть дата публикации, а часть пока в черновиках:
Если отсортировать по дате, возникает вопрос: куда положить строки с
NULL? В начало или в конец?Разные базы данных ведут себя по-разному.
В PostgreSQL:
ASCзначенияNULLобычно идут в конце;DESCзначенияNULLобычно идут в начале.В MySQL:
ASCзначенияNULLобычно идут в начале;DESCзначенияNULLобычно идут в конце.Если ты работаешь в PostgreSQL и хочешь явно управлять положением
NULL, используйNULLS FIRSTилиNULLS LAST.Например, показать сначала опубликованные статьи от свежих к старым, а черновики без даты оставить в конце:
SELECT title, published_at FROM articles ORDER BY published_at DESC NULLS LAST;А если нужно наоборот — строки без даты поднять наверх:
SELECT title, published_at FROM articles ORDER BY published_at DESC NULLS FIRST;В MySQL синтаксиса
NULLS FIRSTиNULLS LASTнет. Там часто используют выражение:SELECT title, published_at FROM articles ORDER BY published_at IS NULL, published_at DESC;published_at IS NULLвернёт условный признак: дата отсутствует или нет. Благодаря этому можно управлять тем, куда попадут строки с пустой датой.ORDER BY после WHERE
ORDER BYвыполняется после фильтрации.Например, нужно взять только оплаченные заказы и отсортировать их от новых к старым:
SELECT id, customer_id, amount, created_at FROM orders WHERE status = 'paid' ORDER BY created_at DESC;Сначала
WHEREоставит только оплаченные заказы. ПотомORDER BYотсортирует уже отфильтрованный результат.Порядок частей запроса важен:
SELECT columns FROM table_name WHERE condition ORDER BY column_name;Нельзя поставить
ORDER BYпередWHEREилиFROM. SQL ожидает части запроса в своём порядке.ORDER BY после GROUP BY
ORDER BYчасто используют вместе с группировкой.Например, нужно посчитать выручку по каждому пользователю и отсортировать клиентов по сумме покупок:
SELECT customer_id, SUM(amount) AS total_amount FROM orders GROUP BY customer_id ORDER BY total_amount DESC;Здесь происходит несколько шагов:
GROUP BY customer_idсобирает заказы по клиентам.SUM(amount)считает сумму заказов каждого клиента.ORDER BY total_amount DESCставит клиентов с самой большой суммой наверх.Так получаются рейтинги, топы и отчёты.
Ещё пример: найти самые активные категории товаров.
SELECT category_id, COUNT(*) AS products_count FROM products GROUP BY category_id ORDER BY products_count DESC;Сначала считаем количество товаров в каждой категории, потом сортируем категории по этому количеству.
Можно ли сортировать по колонке, которой нет в SELECT
Обычно да.
Например:
SELECT title FROM books ORDER BY rating DESC;В результате мы выводим только
title, но сортируем поrating. Во многих базах данных это нормальная ситуация.Это удобно, когда колонка нужна только для порядка, но не нужна пользователю в выдаче.
Однако есть важный нюанс с
DISTINCT.Например:
SELECT DISTINCT author FROM books ORDER BY rating DESC;Такой запрос может быть проблемным. Почему?
DISTINCTоставляет уникальных авторов. Но у одного автора может быть несколько книг с разными рейтингами. База не всегда может понять, какой именноratingиспользовать для сортировки автора.Поэтому с
DISTINCTлучше сортировать по тем выражениям, которые есть в результате, или сначала аккуратно агрегировать данные.Например:
SELECT author, MAX(rating) AS best_rating FROM books GROUP BY author ORDER BY best_rating DESC;Теперь всё честно: для каждого автора мы посчитали лучший рейтинг и сортируем именно по нему.
ORDER BY и UNION
Если ты объединяешь результаты через
UNION, сортировку обычно пишут один раз в самом конце.Например:
SELECT name, email FROM customers UNION SELECT name, email FROM partners ORDER BY name;Здесь
ORDER BY nameсортирует общий результат после объединения.Не нужно ставить отдельный
ORDER BYв каждую часть запроса, если тебе нужен итоговый порядок всей выдачи.Правильная мысль такая: сначала собрать общий список, потом отсортировать его.
Числа, сохранённые как строки
Одна из неприятных ошибок новичков — хранить числа в текстовой колонке.
Представь, что цены лежат не в числовом типе, а в строковом:
Запрос:
SELECT title, price_text FROM products ORDER BY price_text;Может дать такой порядок:
Для человека это странно: 2 должно быть раньше 10. Но для строки всё логично: база сравнивает текст посимвольно, а не как числа.
Чтобы отсортировать как числа, можно привести тип:
SELECT title, price_text FROM products ORDER BY CAST(price_text AS INTEGER);Но лучше хранить числа в числовых колонках, а не в строках. Тогда сортировка, фильтрация и расчёты будут работать нормально.
ORDER BY по номеру колонки
SQL позволяет сортировать по номеру колонки из
SELECT.Например:
SELECT title, rating FROM books ORDER BY 2 DESC;2означает вторую колонку в результате, то естьrating.Запрос сработает. Но для учебного и рабочего кода это не лучший стиль.
Почему?
Потому что такой запрос хуже читается. Нужно глазами считать, какая колонка в
SELECTпервая, какая вторая. А если кто-то поменяет порядок колонок, сортировка может случайно измениться.Лучше писать явно:
SELECT title, rating FROM books ORDER BY rating DESC;Так понятнее и тебе, и человеку, который будет читать запрос после тебя.
Частые ошибки с ORDER BY
Думать, что порядок есть сам по себе
Самая частая ошибка: считать, что база вернёт строки в порядке добавления.
Например:
SELECT id, title FROM posts;Может показаться, что посты будут идти по
id. Но SQL этого не обещает.Нужен порядок по
id— напиши явно:SELECT id, title FROM posts ORDER BY id;Нужны свежие посты — тоже явно:
SELECT id, title, created_at FROM posts ORDER BY created_at DESC, id DESC;Использовать LIMIT без ORDER BY
Плохо:
SELECT id, title FROM posts LIMIT 10;Так ты получишь просто 10 строк. Не топ, не последние, не первые по смыслу.
Хорошо:
SELECT id, title, created_at FROM posts ORDER BY created_at DESC, id DESC LIMIT 10;Теперь это действительно 10 последних постов.
Забыть направление сортировки
Если написать просто
ORDER BY created_at, база отсортирует по возрастанию. То есть сначала будут самые старые даты.Для ленты новостей обычно нужно наоборот:
SELECT title, created_at FROM posts ORDER BY created_at DESC;Не добавить второй критерий при одинаковых значениях
Плохо:
SELECT id, title, rating FROM books ORDER BY rating DESC;Если у нескольких книг одинаковый рейтинг, порядок между ними не гарантирован.
Лучше:
SELECT id, title, rating FROM books ORDER BY rating DESC, id ASC;Теперь сортировка стабильнее.
Сортировать текстовые числа как настоящие числа
Если колонка строковая, сортировка будет строковой:
SELECT title, price_text FROM products ORDER BY price_text;Если данные уже такие, можно привести тип:
SELECT title, price_text FROM products ORDER BY CAST(price_text AS INTEGER);Но правильнее хранить цену в числовой колонке.
Главное
ORDER BYнужен, чтобы задать понятный и предсказуемый порядок строк в результате запроса.Без него база не гарантирует порядок. Даже если тебе кажется, что строки «и так идут нормально», это случайность, а не правило.
Главные шаблоны:
SELECT title, price FROM products ORDER BY price;SELECT title, price FROM products ORDER BY price DESC;SELECT name, department, salary FROM employees ORDER BY department ASC, salary DESC;SELECT title, created_at FROM posts ORDER BY created_at DESC, id DESC LIMIT 10;Запомни:
ASC— по возрастанию;DESC— по убыванию;ASCможно не писать, он используется по умолчанию;ORDER BYдают многоуровневую сортировку;ORDER BYвместе сLIMITпозволяет делать топы и последние записи;id;NULLи строковыми числами нужно быть особенно внимательным.ORDER BY— это не украшение запроса, а способ превратить набор строк в понятный список. Именно с него начинается нормальная выдача: товары становятся отсортированными, отчёты — читаемыми, ленты — свежими, а топы — честными.