По умолчанию SQL не гарантирует никакого порядка строк в результате запроса. Каждый раз строки могут приходить в разном порядке — в зависимости от того как СУБД хранит данные внутри. Чтобы результат был предсказуемым, используется ORDER BY.
SELECT что_выбрать
FROM таблица
WHERE условие
ORDER BY столбец;По умолчанию сортировка идёт по возрастанию — от меньшего к большему, от А до Я. Это можно указать явно через ASC (ascending), или опустить — результат одинаков:
-- Товары от самого дешёвого к самому дорогому
SELECT name, price FROM products
ORDER BY price ASC;
-- ASC можно не писать — результат тот же
SELECT name, price FROM products
ORDER BY price;┌──────────────────────────────┬──────────┐
│ name │ price │
├──────────────────────────────┼──────────┤
│ Футболка хлопок M │ 800.00 │
│ Футболка хлопок L │ 800.00 │
│ Грокаем алгоритмы │ 1100.00 │
│ Чистый код. Мартин │ 1200.00 │
│ Паттерны проектирования │ 1400.00 │
│ ... │ ... │
└──────────────────────────────┴──────────┘
DESC (descending) — сортировка по убыванию, от большего к меньшему:
-- Товары от самого дорогого к самому дешёвому
SELECT name, price FROM products
ORDER BY price DESC;┌──────────────────────────────┬──────────┐
│ name │ price │
├──────────────────────────────┼──────────┤
│ Ноутбук Lenovo IdeaPad │ 75000.00 │
│ Ноутбук ASUS VivoBook │ 68000.00 │
│ Планшет Apple iPad │ 55000.00 │
│ Смартфон Samsung Galaxy │ 45000.00 │
│ ... │ ... │
└──────────────────────────────┴──────────┘
Можно сортировать сразу по нескольким столбцам — перечисляем их через запятую. Второй столбец применяется только тогда, когда значения первого совпадают:
-- Сначала сортируем по category_id (по возрастанию),
-- внутри каждой категории — по цене (по убыванию)
SELECT name, price, category_id FROM products
ORDER BY category_id ASC, price DESC;┌──────────────────────────────┬──────────┬─────────────┐
│ name │ price │ category_id │
├──────────────────────────────┼──────────┼─────────────┤
│ Ноутбук Lenovo IdeaPad │ 75000.00 │ 1 │
│ Ноутбук ASUS VivoBook │ 68000.00 │ 1 │
│ Планшет Apple iPad │ 55000.00 │ 1 │
│ Смартфон Samsung Galaxy │ 45000.00 │ 1 │
│ Наушники Sony WH-1000XM5 │ 28000.00 │ 1 │
│ Смартфон Xiaomi Redmi │ 22000.00 │ 1 │
│ Монитор LG 27" │ 32000.00 │ 2 │
│ Клавиатура Keychron K2 │ 8900.00 │ 2 │
│ ... │ ... │ ... │
└──────────────────────────────┴──────────┴─────────────┘
Каждый столбец в ORDER BY имеет свой независимый ASC/DESC. Можно написать ORDER BY category_id ASC, price DESC — и это будет работать именно так.
ORDER BY всегда идёт после WHERE:
-- Пользователи из Москвы, отсортированные по имени
SELECT name, city FROM users
WHERE city = 'Москва'
ORDER BY name ASC;┌──────────────────┬────────┐
│ name │ city │
├──────────────────┼────────┤
│ Алексей Смирнов │ Москва │
│ Андрей Борисов │ Москва │
│ Анна Волкова │ Москва │
│ Дмитрий Козлов │ Москва │
│ Иван Попов │ Москва │
└──────────────────┴────────┘
Порядок написания клаузул:
SELECT→FROM→WHERE→ORDER BY. Нарушение этого порядка приведёт к ошибке синтаксиса. Этот порядок нужно запомнить — он будет только расширяться по мере изучения новых команд.
Когда в таблице тысячи строк, возвращать их все сразу — неэффективно. LIMIT ограничивает количество строк в результате:
-- Первые 5 товаров по цене (самые дорогие)
SELECT name, price FROM products
ORDER BY price DESC
LIMIT 5;┌──────────────────────────────┬──────────┐
│ name │ price │
├──────────────────────────────┼──────────┤
│ Ноутбук Lenovo IdeaPad │ 75000.00 │
│ Ноутбук ASUS VivoBook │ 68000.00 │
│ Планшет Apple iPad │ 55000.00 │
│ Смартфон Samsung Galaxy │ 45000.00 │
│ Кресло офисное Hara │ 32000.00 │
└──────────────────────────────┴──────────┘
LIMITбезORDER BYвернёт произвольные строки — порядок не определён. Почти всегда они используются вместе.
OFFSET указывает сколько строк пропустить перед началом выборки. Вместе с LIMIT это основа пагинации — разбивки результатов на страницы:
-- Страница 1: строки 1–5
SELECT name, price FROM products
ORDER BY price DESC
LIMIT 5 OFFSET 0;
-- Страница 2: строки 6–10
SELECT name, price FROM products
ORDER BY price DESC
LIMIT 5 OFFSET 5;
-- Страница 3: строки 11–15
SELECT name, price FROM products
ORDER BY price DESC
LIMIT 5 OFFSET 10;Логика простая: OFFSET = (номер_страницы - 1) * размер_страницы.
Посмотрим на страницу 2 в нашей базе:
┌──────────────────────────────┬──────────┐
│ name │ price │
├──────────────────────────────┼──────────┤
│ Монитор LG 27" │ 32000.00 │
│ Наушники Sony WH-1000XM5 │ 28000.00 │
│ Смартфон Xiaomi Redmi │ 22000.00 │
│ Стол компьютерный IKEA │ 15000.00 │
│ Стол компьютерный IKEA │ 15000.00 │
└──────────────────────────────┴──────────┘
Пагинация — стандартный паттерн в любом веб-приложении: "показать 20 товаров на странице", "следующие 20" и так далее. В будущем когда мы будем строить API, этот механизм окажется очень знакомым.
Псевдоним (AS) позволяет временно переименовать столбец или таблицу в рамках одного запроса. Это не меняет реальные имена в базе данных — только то, как они отображаются в результате.
-- Без псевдонима — столбец называется "price"
SELECT name, price FROM products;
-- С псевдонимом — столбец называется "Цена (руб.)"
SELECT name, price AS "Цена (руб.)" FROM products;┌──────────────────────────────┬──────────────┐
│ name │ Цена (руб.) │
├──────────────────────────────┼──────────────┤
│ Ноутбук Lenovo IdeaPad │ 75000.00 │
│ Ноутбук ASUS VivoBook │ 68000.00 │
│ ... │ ... │
└──────────────────────────────┴──────────────┘
Псевдонимы особенно полезны для вычисляемых выражений — когда столбец не существует в таблице, а создаётся прямо в запросе:
-- Вычисляем стоимость всего остатка по каждому товару
SELECT
name,
price,
stock,
price * stock AS total_value
FROM products
ORDER BY total_value DESC;┌──────────────────────────────┬──────────┬───────┬─────────────┐
│ name │ price │ stock │ total_value │
├──────────────────────────────┼──────────┼───────┼─────────────┤
│ Футболка хлопок M │ 800.00 │ 200 │ 160000.00 │
│ Футболка хлопок L │ 800.00 │ 180 │ 144000.00 │
│ Чистый код. Мартин │ 1200.00 │ 100 │ 120000.00 │
│ Грокаем алгоритмы │ 1100.00 │ 90 │ 99000.00 │
│ ... │ ... │ ... │ ... │
└──────────────────────────────┴──────────┴───────┴─────────────┘
Здесь price * stock — это арифметическое выражение прямо в SELECT. SQL умеет делать базовые вычисления: +, -, *, /. Псевдоним AS total_value даёт результату понятное имя.
Если псевдоним содержит пробелы или специальные символы — оборачиваем в двойные кавычки:
AS "Общая стоимость". Если псевдоним одно простое слово — кавычки необязательны:AS total_value.
Таблицам тоже можно давать псевдонимы. Сейчас это выглядит избыточным, но станет незаменимым в следующих уроках когда мы начнём соединять несколько таблиц:
-- p — псевдоним для таблицы products
SELECT p.name, p.price FROM products AS p
WHERE p.price > 10000;
-- Ключевое слово AS для таблиц можно опускать
SELECT p.name, p.price FROM products p
WHERE p.price > 10000;Обращение p.name означает "столбец name из таблицы с псевдонимом p". Результат идентичен обычному запросу — псевдоним таблицы здесь просто сокращение.
DISTINCT убирает дублирующиеся строки из результата и оставляет только уникальные:
-- Все города из таблицы пользователей (с повторами)
SELECT city FROM users;Москва
Санкт-Петербург
Москва
Новосибирск
Екатеринбург
Москва
Казань
Санкт-Петербург
Москва
Нижний Новгород
Москва
Екатеринбург
Новосибирск
Санкт-Петербург
Казань
-- Только уникальные города
SELECT DISTINCT city FROM users;┌─────────────────┐
│ city │
├─────────────────┤
│ Москва │
│ Санкт-Петербург │
│ Новосибирск │
│ Екатеринбург │
│ Казань │
│ Нижний Новгород │
└─────────────────┘
DISTINCT не убирает дубли отдельно по каждому столбцу. DISTINCT работает по всей строке целиком — он убирает строки где все перечисленные столбцы одинаковы.
При указании нескольких столбцов уникальность оценивается по их комбинации:
-- Уникальные комбинации user_id + статус заказа
SELECT DISTINCT user_id, status FROM orders
ORDER BY user_id;┌──────────┬───────────┐
│ user_id │ status │
├──────────┼───────────┤
│ 1 │ delivered │
│ 2 │ delivered │
│ 3 │ delivered │
│ 3 │ delivered │
│ 4 │ delivered │
└──────────┴───────────┘
DISTINCTприменяется сразу послеSELECTи действует на все перечисленные столбцы. НаписатьSELECT user_id, DISTINCT status— нельзя, это синтаксическая ошибка.
К этому уроку структура запроса выглядит так:
SELECT [DISTINCT] столбцы
FROM таблица
WHERE условие
ORDER BY столбец [ASC|DESC]
LIMIT n OFFSET m;Этот порядок фиксирован. Нарушить его — значит получить ошибку синтаксиса SQL. Запомните его сейчас — в следующих уроках он будет только дополняться новыми клаузулами.
При этом важно понимать одну особенность SQL: порядок написания запроса и порядок его выполнения — не одно и то же. Хотя запрос начинается с SELECT, СУБД логически обрабатывает его иначе. Сначала определяется источник данных (FROM), затем применяется фильтрация (WHERE), и только после этого формируется итоговый набор столбцов (SELECT).
Именно поэтому псевдонимы столбцов, созданные в SELECT, недоступны внутри WHERE того же запроса. Например:
SELECT name,
price * stock AS total_value
FROM products
WHERE total_value > 100000;Такой запрос вызовет ошибку, потому что на момент выполнения WHERE псевдоним total_value ещё не существует. Нужно повторить само выражение:
SELECT name,
price * stock AS total_value
FROM products
WHERE price * stock > 100000;Логический порядок обработки запроса:
1. FROM — выбираются таблицы и выполняются соединения (JOIN)
2. WHERE — фильтруются строки по условиям
3. SELECT — формируются столбцы результата, создаются псевдонимы
4. DISTINCT — удаляются дубликаты строк
5. ORDER BY — сортировка результата
6. LIMIT/OFFSET — ограничение количества строк
То есть, хотя SELECT в синтаксисе пишется первым, СУБД сначала смотрит FROM и WHERE. Именно поэтому псевдонимы, созданные в SELECT, не работают в WHERE, но могут использоваться в ORDER BY.
- Что вернёт
SELECT * FROM products ORDER BY priceбез указанияASCилиDESC? - Зачем использовать
LIMITвместе сORDER BY? Что произойдёт если написать толькоLIMIT 5без сортировки? - Как получить строки с 11 по 20 из отсортированной таблицы используя
LIMITиOFFSET? - В чём разница между
SELECT DISTINCT city FROM usersиSELECT city FROM users? - Можно ли указать разные направления сортировки для разных столбцов в одном
ORDER BY? - Что делает псевдоним
AS— переименовывает столбец в таблице или только в результате запроса? - Можно ли использовать псевдоним столбца в условии
WHEREтого же запроса? - Как
DISTINCTобрабатывает несколько столбцов? Например:SELECT DISTINCT category_id, stock FROM products. - Зачем нужны псевдонимы для таблиц если можно писать полное имя таблицы?
Выведите все товары отсортированные по названию в алфавитном порядке. Покажите только name и price.
Найдите 3 самых новых пользователя (по дате регистрации). Выведите имя и дату регистрации.
Выведите уникальные статусы из таблицы orders. Отсортируйте их в алфавитном порядке.
Выведите товары дороже 20 000 рублей, отсортированные по цене от дорогих к дешёвым. Покажите только первые 5. Выведите name и price.
Для каждого товара вычислите общую стоимость складского остатка (price * stock). Выведите name, price, stock и вычисляемый столбец с псевдонимом stock_value. Отсортируйте по stock_value по убыванию.
Реализуйте пагинацию для таблицы products отсортированной по id. Выведите вторую страницу при условии что на каждой странице по 8 товаров.
Выведите уникальные города пользователей в обратном алфавитном порядке (от Я до А).
Найдите 5 товаров с наименьшим остатком на складе. Выведите name, stock и category_id. При одинаковом остатке — сортируйте по названию в алфавитном порядке.
Выведите name и price всех товаров с псевдонимами: name → Товар, price → Стоимость. Отсортируйте по цене по убыванию, покажите только первые 10 строк.
Найдите товары из категорий 1 и 2 (электроника и периферия) стоимостью не выше 30 000 рублей. Отсортируйте по убыванию цены, затем по возрастанию остатка. Выведите name, price, stock. Покажите только первые 5 результатов.