Skip to content

Latest commit

 

History

History
452 lines (336 loc) · 22.9 KB

File metadata and controls

452 lines (336 loc) · 22.9 KB

Урок 4. Сортировка, ограничение, псевдонимы

ORDER BY — сортировка результата

По умолчанию SQL не гарантирует никакого порядка строк в результате запроса. Каждый раз строки могут приходить в разном порядке — в зависимости от того как СУБД хранит данные внутри. Чтобы результат был предсказуемым, используется ORDER BY.

SELECT что_выбрать
FROM таблица
WHERE условие
ORDER BY столбец;

ASC и DESC

По умолчанию сортировка идёт по возрастанию — от меньшего к большему, от А до Я. Это можно указать явно через 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 вместе

ORDER BY всегда идёт после WHERE:

-- Пользователи из Москвы, отсортированные по имени
SELECT name, city FROM users
WHERE city = 'Москва'
ORDER BY name ASC;
┌──────────────────┬────────┐
│ name             │ city   │
├──────────────────┼────────┤
│ Алексей Смирнов  │ Москва │
│ Андрей Борисов   │ Москва │
│ Анна Волкова     │ Москва │
│ Дмитрий Козлов   │ Москва │
│ Иван Попов       │ Москва │
└──────────────────┴────────┘

Порядок написания клаузул: SELECTFROMWHEREORDER BY. Нарушение этого порядка приведёт к ошибке синтаксиса. Этот порядок нужно запомнить — он будет только расширяться по мере изучения новых команд.


LIMIT и OFFSET — ограничение выборки

Когда в таблице тысячи строк, возвращать их все сразу — неэффективно. 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 — смещение

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 — псевдонимы

Псевдоним (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 — уникальные значения

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.


Вопросы

  1. Что вернёт SELECT * FROM products ORDER BY price без указания ASC или DESC?
  2. Зачем использовать LIMIT вместе с ORDER BY? Что произойдёт если написать только LIMIT 5 без сортировки?
  3. Как получить строки с 11 по 20 из отсортированной таблицы используя LIMIT и OFFSET?
  4. В чём разница между SELECT DISTINCT city FROM users и SELECT city FROM users?
  5. Можно ли указать разные направления сортировки для разных столбцов в одном ORDER BY?
  6. Что делает псевдоним AS — переименовывает столбец в таблице или только в результате запроса?
  7. Можно ли использовать псевдоним столбца в условии WHERE того же запроса?
  8. Как DISTINCT обрабатывает несколько столбцов? Например: SELECT DISTINCT category_id, stock FROM products.
  9. Зачем нужны псевдонимы для таблиц если можно писать полное имя таблицы?

Задачи

Задача 1.

Выведите все товары отсортированные по названию в алфавитном порядке. Покажите только name и price.


Задача 2.

Найдите 3 самых новых пользователя (по дате регистрации). Выведите имя и дату регистрации.


Задача 3.

Выведите уникальные статусы из таблицы orders. Отсортируйте их в алфавитном порядке.


Задача 4.

Выведите товары дороже 20 000 рублей, отсортированные по цене от дорогих к дешёвым. Покажите только первые 5. Выведите name и price.


Задача 5.

Для каждого товара вычислите общую стоимость складского остатка (price * stock). Выведите name, price, stock и вычисляемый столбец с псевдонимом stock_value. Отсортируйте по stock_value по убыванию.


Задача 6.

Реализуйте пагинацию для таблицы products отсортированной по id. Выведите вторую страницу при условии что на каждой странице по 8 товаров.


Задача 7.

Выведите уникальные города пользователей в обратном алфавитном порядке (от Я до А).


Задача 8.

Найдите 5 товаров с наименьшим остатком на складе. Выведите name, stock и category_id. При одинаковом остатке — сортируйте по названию в алфавитном порядке.


Задача 9.

Выведите name и price всех товаров с псевдонимами: nameТовар, priceСтоимость. Отсортируйте по цене по убыванию, покажите только первые 10 строк.


Задача 10.

Найдите товары из категорий 1 и 2 (электроника и периферия) стоимостью не выше 30 000 рублей. Отсортируйте по убыванию цены, затем по возрастанию остатка. Выведите name, price, stock. Покажите только первые 5 результатов.


Предыдущий урок | Следующий урок