homework, pet project DE Dmitry_Medvedev #1

Open
dmitry_medvedev wants to merge 264 commits from md_branch into main
Showing only changes of commit bcc5f43d56 - Show all commits
+173
View File
@@ -0,0 +1,173 @@
--Задача 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: внутри подзапроса или снаружи, и почему?
Легче фильтровать внешне, потому что можно создать нескоько внешних запросов, изменив лишь фильтр.
*/