155 lines
4.1 KiB
SQL
155 lines
4.1 KiB
SQL
-- Задание 1
|
|
SELECT
|
|
name
|
|
FROM
|
|
mnoskov.users
|
|
WHERE id IN (
|
|
SELECT user_id
|
|
FROM mnoskov.orders
|
|
)
|
|
ORDER BY
|
|
name;
|
|
-- Тут не приходится делать JOIN и DISTINCT, а просто ищем айдишник в списке
|
|
-- N (подзапрос) может быть более читаемым, когда подзапрос очень простой и короткий
|
|
|
|
|
|
-- Задание 2
|
|
SELECT
|
|
u.id,
|
|
u.name,
|
|
u.email
|
|
FROM
|
|
mnoskov.users u
|
|
WHERE NOT EXISTS (
|
|
SELECT user_id
|
|
FROM mnoskov.orders o
|
|
WHERE
|
|
u.id = o.user_id
|
|
)
|
|
-- NOT EXISTS безопаснее в плане NULL, и часто планируется лучше чем NOT IN
|
|
-- Определить какое количество людей не делают заказы, и кинуть им например купон на скидку на первый заказ
|
|
|
|
|
|
-- Задание 3
|
|
SELECT
|
|
u.name,
|
|
(
|
|
SELECT
|
|
count(*)
|
|
FROM
|
|
mnoskov.orders o
|
|
WHERE
|
|
u.id = o.user_id
|
|
) AS orders_count
|
|
FROM
|
|
mnoskov.users u
|
|
ORDER BY
|
|
orders_count DESC, u.name;
|
|
-- Подзапрос в SELECT не может возвращать несколько строк, потому что SELECT ожидает толбко одну
|
|
-- Как изменится сложность запроса, если пользователей станет, например, 1 млн? Не совсем понял этот вопрос, но думаю запрос просто будет выполняться дольше
|
|
|
|
|
|
-- Задание 4
|
|
WITH revenue_user AS (
|
|
SELECT
|
|
o.user_id,
|
|
sum(oi.quantity * oi.price_at_purchase) AS total_revenue
|
|
FROM
|
|
mnoskov.orders o
|
|
JOIN
|
|
mnoskov.order_items oi ON o.id = oi.order_id
|
|
GROUP BY
|
|
o.user_id
|
|
)
|
|
SELECT
|
|
u.name,
|
|
ru.total_revenue
|
|
FROM
|
|
mnoskov.users u
|
|
JOIN
|
|
revenue_user ru ON u.id = ru.user_id
|
|
ORDER BY
|
|
ru.total_revenue desc;
|
|
-- Проще читать когда шаг «посчитать выручку по пользователям» вынесен отдельно
|
|
-- Чтобы избежать дублирования импользуется CTE, а не простой подзапрос
|
|
|
|
|
|
-- Задание 5
|
|
WITH revenue_product AS (
|
|
SELECT
|
|
product_id,
|
|
sum(quantity * price_at_purchase) AS product_revenue
|
|
FROM
|
|
mnoskov.order_items
|
|
GROUP BY
|
|
product_id
|
|
)
|
|
SELECT
|
|
p.name,
|
|
p.category,
|
|
rp.product_revenue
|
|
FROM
|
|
mnoskov.products p
|
|
JOIN
|
|
revenue_product rp ON p.id = rp.product_id
|
|
ORDER BY
|
|
rp.product_revenue
|
|
LIMIT 3;
|
|
-- Сначала удобнее считать агрегаты, чтобы код не выгляжел грооздким и чтобы в нем было легче разобраться
|
|
-- Отличае в удобстве чтения и написания. И этот агрегат можно потом ещё и переиспользовать
|
|
|
|
|
|
-- Задание 6
|
|
WITH revenue_user AS (
|
|
SELECT
|
|
o.user_id,
|
|
sum(oi.quantity * oi.price_at_purchase) AS total_revenue
|
|
FROM
|
|
mnoskov.order_items oi
|
|
JOIN
|
|
mnoskov.orders o ON oi.order_id = o.id
|
|
GROUP BY
|
|
o.user_id
|
|
)
|
|
SELECT
|
|
u.name,
|
|
u.email,
|
|
ru.total_revenue
|
|
FROM
|
|
mnoskov.users u
|
|
JOIN
|
|
revenue_user ru ON u.id = ru.user_id
|
|
WHERE
|
|
u.is_active = TRUE AND
|
|
ru.total_revenue > 10000
|
|
ORDER BY
|
|
ru.total_revenue DESC, u.name;
|
|
-- Я бы назвал этот сегмегт - frequent_buyers
|
|
-- Таким пользователям надо предлагать какую ниюудь специальную программу лояльности
|
|
|
|
|
|
-- Задание 7
|
|
WITH revenue_order AS (
|
|
SELECT
|
|
order_id,
|
|
sum(quantity * price_at_purchase) AS order_revenue
|
|
FROM
|
|
mnoskov.order_items
|
|
GROUP BY
|
|
order_id
|
|
)
|
|
SELECT
|
|
ro.order_id,
|
|
o.order_date::date,
|
|
ro.order_revenue
|
|
FROM
|
|
mnoskov.orders o
|
|
JOIN
|
|
revenue_order ro ON o.id = ro.order_id
|
|
WHERE
|
|
o.status = 'paid' AND
|
|
ro.order_revenue < 10000
|
|
ORDER BY
|
|
ro.order_revenue;
|
|
-- Причины: злоупотребление промокодами, ошибки
|
|
-- По 'paid' удобнее фильтровать снаржуи, потому тут мы подключаем orders, а подключать orders ещё и внутри было бы бессмысленно |