Files
homework/dmedvedev/underSelect and CTE.sql
T
2026-06-07 23:20:00 +05:00

173 lines
7.4 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
--Задача 1. Пользователи с заказами
--task 1
SELECT
u.name
FROM users u
WHERE id IN (
SELECT o.user_id
FROM orders o
)
ORDER BY u.name;
/*
Чем такой подход отличается от варианта с JOIN + DISTINCT по users?
Принципиальная разница в том , что в этом варианте сначала выполняется подзапрос,
который выдает айди, потом уже выполняется внешний селект. И дубликаты не нужно удалять.
В каком случае IN (подзапрос) может быть более читаемым, чем JOIN?
В том случае, если необходимо найти существование записи в таблице или же когда нужна только одна колонка из таблицы.
*/
--task 2
--Задача 2. Пользователи без заказов (NOT EXISTS)
SELECT
u.id,
u.name,
u.email
FROM users u
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE u.id = o.user_id
);
/*
Почему для этой задачи NOT EXISTS предпочтительнее, чем NOT IN (SELECT user_id ...)?
Потому что NOT EXISTS показывает факт существования запиши для определенного условия
и не перебирает все дублирующие строки, а использует только первую попавшуюся.
Как бизнес может использовать список таких пользователей?
Так как эти пользователи не завершили процесс покупки, менеджерам необходимо будет поработать
с такими клиентами, предложить скидки, какие-то другие предложения.
*/
--task 3
--Задача 3. Сколько заказов у каждого пользователя (подзапрос в SELECT)
SELECT
name,
(SELECT COUNT(*)
FROM orders o
WHERE o.user_id = u.id
) AS orders_count
FROM users u
ORDER BY orders_count DESC, name DESC;
/*
Почему подзапрос в SELECT не может возвращать несколько строк?
Потому что таково его свойство. Если же после селекта поставить наименование колонки
и если будет у пользователя несколько заказов, то выдаст ошибку, поэтому нужно либо
агрегировать, либо ограничить лимитом 1.
Как изменится сложность запроса, если пользователей станет, например, 1 млн?
В таком случае SQL будет долго обрабатывать данные, потому что нужно будет обработать миллион строк в одном селекте.
В таких случаях лучше использовать групп бай.
*/
--task 4
--Задача 4. Сумма выручки по каждому пользователю (подзапрос или CTE)
WITH revenie_user AS (
SELECT
o.user_id,
SUM(oi.quantity * oi.price_at_purchase) AS total_revenue
FROM orders o
LEFT JOIN order_items oi ON o.id = oi.order_id
GROUP BY o.user_id
)
SELECT
u.name,
revenie_user.total_revenue
FROM users u
LEFT JOIN revenie_user ON u.id = revenie_user.user_id;
/*
Как проще читать такой запрос: когда шаг «посчитать выручку по пользователям» вынесен отдельно, или когда всё написано в одном SELECT?
Конечно проще читать, когда делается через СТЕ.
Если эту «выручку по пользователям» придётся использовать в другом отчёте, как избежать дублирования кода?
Может быть завернуть это еще в одну СТЕ?
*/
--task 5
--Задача 5. Топ‑3 товара по выручке (CTE)
WITH product_revenue AS (
SELECT
product_id,
SUM(quantity * price_at_purchase) AS revenue_pr
FROM order_items oi
GROUP BY product_id
)
SELECT
p.name AS product_name,
p.category AS category,
product_revenue.revenue_pr AS product_revenue
FROM products p
LEFT JOIN product_revenue ON p.id = product_revenue.product_id
ORDER BY product_revenue DESC
LIMIT 3;
/*
Почему удобно сначала считать агрегаты по товарам, а потом уже подтягивать имена/категории?
Потому что в категории может быть несколько разных товаров и если мы начнем агрегировать по категориям,
то можем потерять некоторые товары.
В чём отличие подхода “через CTE” от простого GROUP BY в одном SELECT?
Отличие в том, что здесь мы выполняем два запроса, также СТЕ можно переиспользовать, так как это именованная
таблица, улучшается читаемость запроса.
*/
--task 6
--Задача 6. Активные пользователи с высокой суммой заказов
WITH revenue AS(
SELECT
user_id,
SUM(quantity * price_at_purchase) AS total_revenue
FROM orders o
LEFT JOIN order_items oi ON o.id = oi.order_id
GROUP BY user_id
)
SELECT
name,
email,
revenue.total_revenue
FROM users u
LEFT JOIN revenue ON u.id = revenue.user_id
WHERE is_active IS TRUE AND revenue.total_revenue > 1000;
/*
Как бы ты назвал этот сегмент пользователей на языке продукта (VIP, high‑spenders и т.п.)?
High-revenue active clients
Что можно делать с таким сегментом: какие типы акций/коммуникаций?
Можно предлагать для них какие-либо скидки на другие товары, потому что сразу становится понятно,
что они заинтересованы в нащих продуктах.
*/
--task 7
--Задача 7. «Подозрительно маленькие» оплаченные заказы
WITH revenue AS(
SELECT
order_id,
SUM(quantity * price_at_purchase) AS order_revenue
FROM order_items oi
GROUP BY order_id
)
SELECT
order_id,
order_date,
order_revenue
FROM orders o
LEFT JOIN revenue r ON r.order_id = o.id
WHERE status = 'paid' AND order_revenue < 100;
/*
Какие реальные причины могут быть у таких заказов (ошибки, тесты, злоупотребления промокодами)?
Все вышеперечисленные, а также мошенничество, взлом системы.
На каком шаге выгоднее фильтровать по статусу paid: внутри подзапроса или снаружи, и почему?
Легче фильтровать внешне, потому что можно создать нескоько внешних запросов, изменив лишь фильтр.
*/