added underSELECT and CTE tasks
This commit is contained in:
@@ -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: внутри подзапроса или снаружи, и почему?
|
||||
Легче фильтровать внешне, потому что можно создать нескоько внешних запросов, изменив лишь фильтр.
|
||||
*/
|
||||
Reference in New Issue
Block a user