diff --git a/dmedvedev/underSelect and CTE.sql b/dmedvedev/underSelect and CTE.sql new file mode 100644 index 0000000..61bbca9 --- /dev/null +++ b/dmedvedev/underSelect and CTE.sql @@ -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: внутри подзапроса или снаружи, и почему? +Легче фильтровать внешне, потому что можно создать нескоько внешних запросов, изменив лишь фильтр. +*/ \ No newline at end of file