--Задача 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: внутри подзапроса или снаружи, и почему? Легче фильтровать внешне, потому что можно создать нескоько внешних запросов, изменив лишь фильтр. */