Files
homework/dmezentsev/postgresql/homework/Query_order.md
T
2026-05-24 11:32:46 +03:00

14 KiB
Executable File
Raw Blame History

Задача 1 — Запрос с группировкой

SELECT country, COUNT(*) AS users_count
FROM users
WHERE is_active = true
GROUP BY country
HAVING COUNT(*) > 1
ORDER BY users_count DESC;

1. Когда применится WHERE — до или после GROUP BY?

WHERE применяется ДО GROUP BY

2. Когда применится HAVING — до или после GROUP BY?

HAVING применяется ПОСЛЕ GROUP BY

3. Можно ли использовать алиас users_count в HAVING?

HAVING выполняется до SELECT. Алиас users_count создаётся только на этапе SELECT. Поэтому на момент выполнения HAVING алиас ещё не существует.

4. Можно ли использовать алиас users_count в ORDER BY?

ORDER BY выполняется после SELECT. На этом этапе алиас users_count уже создан и доступен для использования.

Задача 2 — JOIN с фильтрацией

SELECT u.name, o.order_date, o.status
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.country = 'Россия' AND o.status = 'completed'
ORDER BY o.order_date DESC;

1. Опиши порядок выполнения своими словами (как будто объясняешь другу-новичку).

Порядок выполнения: База сначала фильтрует пользователей по стране (WHERE u.country = 'Россия'), оставляя только пользователей из России. Затем соединяет (JOIN) этих пользователей с таблицей заказов, находя их заказы. После этого фильтрует заказы по статусу (WHERE o.status = 'completed'). Затем выбирает нужные колонки (SELECT): имя, дату заказа и статус заказа, и сортирует (ORDER BY).

Задача 3 — Найди ошибку

Дан запрос:

SELECT country, AVG(age) AS avg_age
FROM users
GROUP BY country
HAVING country = 'Россия';

1. Запрос работает, но написан неэффективно. Почему?

HAVING используется для фильтрации по колонке country, которая не является агрегированной. Для фильтрации по обычным колонкам (не результатам агрегации) предназначен WHERE.

2. Перепиши его правильно, используя WHERE.

SELECT country, AVG(age) AS avg_age
FROM users
WHERE country = 'Россия'
GROUP BY country;

3. Объясни, почему твой вариант быстрее.

Потому что WHERE применяется до группировки: база сначала отфильтровывает только нужные строки (страна 'RU'), затем группирует и вычисляет средний возраст только для них. Это уменьшает объём данных для группировки, база может использовать индекс на country и не делает лишних вычислений

Задача 4 — Исправь запрос

SELECT category, AVG(price) AS avg_price
FROM products
GROUP BY category
HAVING category = 'Электроника' AND AVG(price) > 20000;

1. Перепиши запрос так, чтобы часть условий была в WHERE.

SELECT category, AVG(price) AS avg_price
FROM products
WHERE category = 'Электроника'
GROUP BY category
HAVING AVG(price) > 20000;

2. Объясни, почему это быстрее.

Это быстрее, потому что WHERE отсекает ненужные категории до группировки

3. Нарисуй схему: сколько строк будет обработано в каждом случае.

Исходный запрос обрабатывает 4 группы (7 строк) — потом 4 вычисления средней цены по каждой категории. Исправленный запрос фильтрует по условию — обрабатывает только 1 группу (3 строки) — 1 вычисление средней цены

Задача 5 — Исправь все ошибки

SELECT 
  country,
  COUNT(*) AS users_count
FROM users
WHERE users_count > 1
GROUP BY country
ORDER BY country;

1. Найди ошибку и исправь запрос.

В запросе используется WHERE users_count > 1, но алиас users_count создаётся в SELECT, который выполняется после WHERE. Кроме того, WHERE не может фильтровать по агрегатным функциям COUNT(*)

SELECT country, COUNT(*) AS users_count
FROM users
GROUP BY country
HAVING COUNT(*) > 1
ORDER BY country;

Задача 6 — Полный цикл выполнения

SELECT 
  u.country,
  COUNT(o.id) AS orders_count,
  AVG(o.id) AS avg_order_id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.is_active = true
GROUP BY u.country
HAVING COUNT(o.id) > 1
ORDER BY orders_count DESC
LIMIT 3;

1. Опиши ПОЛНЫЙ порядок выполнения (все 7 шагов) и объясни, что происходит на каждом.

  • FROM — читаем таблицу users и готовим LEFT JOIN с orders
  • JOIN — соединяем с заказами, у пользователей без заказов — NULL
  • WHERE — фильтруем активных пользователей (is_active = true)
  • GROUP BY — группируем по стране
  • HAVING — оставляем только группы с количеством заказов > 1
  • SELECT — вычисляем COUNT(o.id) и AVG(o.id) для каждой группы
  • ORDER BY — сортируем по убыванию orders_count
  • LIMIT — берём первые 3 строки

Задача 7 — Оптимизация

Сравни два запроса:

-- Запрос А
SELECT category, COUNT(*)
FROM products
GROUP BY category
HAVING category = 'Электроника';

-- Запрос Б
SELECT category, COUNT(*)
FROM products
WHERE category = 'Электроника'
GROUP BY category;

1. Какой запрос быстрее? Почему?

Запрос Б быстрее. Причина: фильтр WHERE category = 'Электроника' применяется до группировки

2. Нарисуй таблицу: на каком шаге сколько строк обрабатывается в каждом запросе.

Показатель Запрос А Запрос Б
Прочитано строк 7 7
Создано групп 4 1
Вычислено COUNT 4 раза 1 раз

3. Выполни оба с EXPLAIN ANALYZE и сравни Execution Time.

У запроса А Rows Removed by Filter: 3 после группировки, у запроса Б фильтр применяется при сканировании таблицы

Задача 8 — Многоэтапная фильтрация

SELECT 
  u.name,
  COUNT(o.id) AS orders_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.country = 'Россия'
  AND u.age > 25
  AND o.status = 'completed'
GROUP BY u.name
HAVING COUNT(o.id) >= 2
ORDER BY orders_count DESC;

1. Когда применяются условия WHERE?

Условия WHERE применяются до группировки — фильтруют отдельные строки (пользователей и заказы) перед тем, как они попадут в GROUP BY

2. Когда применяется условие HAVING?

Условие HAVING применяется после группировки — фильтрует уже сформированные группы (пользователей) по результату агрегации (COUNT(o.id)).

3. В чём разница между фильтрацией по o.status в WHERE и по COUNT(o.id) в HAVING?

WHERE o.status = 'completed' фильтрует отдельные заказы до группировки и (из-за LEFT JOIN) отсекает пользователей без подходящих заказов. HAVING COUNT(o.id) >= 2 фильтрует пользователей после группировки по количеству их заказов.

Задача 9 — JOIN и ORDER BY

SELECT 
  u.name,
  p.name AS product_name,
  p.price
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE u.country = 'Россия'
ORDER BY p.price DESC
LIMIT 5;

1. На каком этапе применяется ORDER BY?

ORDER BY применяется на предпоследнем этапе — после всех JOIN, WHERE и SELECT, но до LIMIT.

2. Почему LIMIT идёт после ORDER BY, а не до?

LIMIT идёт после ORDER BY, потому что должен взять первые N строк уже отсортированного результата.

3. Что будет, если поменять их местами (технически невозможно, но подумай концептуально)?

Концептуально при LIMIT, а потом ORDER BY сначала взяли бы случайные строки (в физическом порядке), а потом их отсортировали бы

Задача 10 — Напиши запрос с нуля

Нужно получить:

  • Список категорий товаров
  • Количество товаров в каждой категории
  • Среднюю цену товаров в категории
  • Только те категории, где средняя цена > 15000
  • Только те категории, где товаров >= 2
  • Отсортировать по средней цене (от большей к меньшей)
  • Взять только топ-3

1. Напиши запрос

SELECT 
  category,
  COUNT(*) AS products_count,
  AVG(price) AS avg_price
FROM dmezentsev.products
GROUP BY category
HAVING AVG(price) > 15000 
   AND COUNT(*) >= 2
ORDER BY avg_price DESC
LIMIT 3;

2. Опиши порядок выполнения

  • FROM — читаем таблицу products (7 строк)
  • GROUP BY — группируем по category (4 группы)
  • HAVING — применяем оба условия (фильтруем группы)
  • SELECT — вычисляем COUNT(*) и AVG(price)
  • ORDER BY — сортируем по avg_price DESC
  • LIMIT — берём первые 3 строки

3. Объясни, почему использовал WHERE или HAVING для каждого условия

В этом запросе нет условий WHERE, потому что нет фильтрации по отдельным строкам. Оба условия работают с результатами агрегации (AVG(price), COUNT(*)), поэтому фильтруются уже в HAVING

4. Выполни с EXPLAIN ANALYZE и посмотри план

Seq Scan on products              -- Читаем все 7 строк таблицы
HashAggregate                     -- Группируем по category (4 группы)
Filter: (avg(price) > 15000 AND count(*) >= 2)  -- Применяем HAVING
Rows Removed by Filter: 3         -- 3 группы отброшены
Sort                              -- Сортируем результат по avg_price DESC
Limit                             -- Ограничиваем 3 строками

Задача 11 — Подзапросы и порядок

SELECT 
  u.name,
  u.age,
  (SELECT COUNT(*) 
   FROM orders o 
   WHERE o.user_id = u.id) AS orders_count
FROM users u
WHERE u.country = 'Россия'
ORDER BY orders_count DESC;

1. Когда выполняется подзапрос в SELECT?

Подзапрос в SELECT выполняется для каждой строки после WHERE u.country = 'Россия', но до ORDER BY

2. Можно ли использовать алиас orders_count в WHERE?

Алиас orders_count нельзя использовать в WHERE, потому что WHERE выполняется до SELECT

3. Можно ли использовать его в ORDER BY?

Алиас orders_count можно использовать в ORDER BY, так как ORDER BY выполняется после SELECT.

4. Перепиши этот запрос через JOIN. Какой вариант быстрее? Проверь с EXPLAIN ANALYZE!

SELECT u.name, u.age, COUNT(o.id) AS orders_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.country = 'RU'
GROUP BY u.id, u.name, u.age
ORDER BY orders_count DESC;

На маленьких данных разница незаметна. Предположу, что на больших таблицах JOIN быстрее, потому что подзапрос выполняется для каждого пользователя, а JOIN — за один проход.