homework, pet project DE Dmitry_Medvedev #1
@@ -168,7 +168,6 @@ WHERE o.status = 'paid';
|
||||
/**❓ Ответь на вопросы:**
|
||||
- что будет, если условие **`status = 'paid'`** написать в **`JOIN`** с **`orders`** (там же, где **`ON`**)? Изменится ли результат сейчас и почему?
|
||||
|
||||
В таком случае все задублируется
|
||||
*/
|
||||
|
||||
--task 9
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
--task 1
|
||||
--Задача 1. Нумерация заказов пользователя
|
||||
|
||||
SELECT
|
||||
o.user_id AS user_id,
|
||||
u.name AS user_name,
|
||||
o.id AS order_id,
|
||||
o.order_date AS order_date,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY o.user_id
|
||||
ORDER BY o.order_date
|
||||
) AS order_number_for_user
|
||||
FROM orders o
|
||||
JOIN users u ON o.user_id = u.id;
|
||||
|
||||
/*
|
||||
В чём отличие такой нумерации от простой сортировки ORDER BY order_date без оконной функции?
|
||||
|
||||
Здесь функция работает внутри определенных строк, в данном случае имени.
|
||||
*/
|
||||
|
||||
|
||||
--task 2
|
||||
--Задача 2. Накопительная выручка по пользователю
|
||||
SELECT
|
||||
o.user_id AS user_id,
|
||||
u.name AS user_name,
|
||||
o.id AS order_id,
|
||||
o.order_date,
|
||||
oi.quantity * oi.price_at_purchase AS order_revenue,
|
||||
SUM(oi.quantity * oi.price_at_purchase) OVER (
|
||||
PARTITION BY o.user_id
|
||||
ORDER BY o.order_date
|
||||
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
) AS running_revenue_for_user
|
||||
FROM orders o
|
||||
JOIN order_items oi ON oi.order_id = o.id
|
||||
JOIN users u ON u.id = o.user_id;
|
||||
|
||||
|
||||
/*
|
||||
Почему для накопительной суммы важно задавать рамку окна (от «самого первого» до «текущего»), а не оставлять значения по умолчанию?
|
||||
Если не задавать рамку окна, то не будет накопительного эффекта, будет просто считаться сумма по строке. И то на самом деле я до конца не понял как она считается, как-будто сумма заказа транслируется на строку с частью заказа.
|
||||
|
||||
Как по такой таблице понять, что пользователь начал тратить больше?
|
||||
Можно дополнительно сделать столбец разности текущего значения от предыдущего и будет видно разницу или же сделать столбец по отклонению от среднего. Правильно мыслю?
|
||||
*/
|
||||
|
||||
--task 3
|
||||
--Задача 3. Рейтинг пользователей по выручке
|
||||
WITH total AS (
|
||||
SELECT
|
||||
o.user_id,
|
||||
SUM(quantity * price_at_purchase) AS total_revenue
|
||||
FROM order_items oi
|
||||
JOIN orders o ON o.id = oi.order_id
|
||||
GROUP BY o.user_id
|
||||
)
|
||||
|
||||
SELECT
|
||||
u.name AS user_name,
|
||||
total_revenue,
|
||||
RANK() OVER (
|
||||
ORDER BY total_revenue
|
||||
) AS revenue_rank,
|
||||
DENSE_RANK() OVER (
|
||||
ORDER BY total_revenue
|
||||
) AS dense_revenue_rank
|
||||
FROM users u
|
||||
JOIN total t ON u.id = t.user_id;
|
||||
/*
|
||||
Чем RANK отличается от DENSE_RANK в случае, когда у двух пользователей одинаковая выручка?
|
||||
|
||||
При RANK будет одно одинаковое место, при dense будут разные ранги.
|
||||
*/
|
||||
|
||||
--task 4
|
||||
--Задача 4. Первый и последний заказ пользователя
|
||||
--task 4
|
||||
--Задача 4. Первый и последний заказ пользователя
|
||||
|
||||
SELECT
|
||||
u.name AS user_name,
|
||||
o.id AS order_id,
|
||||
o.order_date AS order_date,
|
||||
first_value(o.order_date) OVER (
|
||||
PARTITION BY o.user_id
|
||||
ORDER BY o.order_date
|
||||
) AS first_order_date,
|
||||
last_value(o.order_date) OVER (
|
||||
PARTITION BY o.user_id
|
||||
ORDER BY o.order_date
|
||||
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
|
||||
) AS last_order_date
|
||||
FROM orders o
|
||||
JOIN users u ON u.id = o.user_id;
|
||||
|
||||
/*
|
||||
Как можно использовать поля «первый заказ» и «последний заказ» в сегментации клиентов?
|
||||
Можно понять единый ли это заказ или нет. Если единый, то соответственно, других заказов не было и сравнить с текущей датой и понять давно ли вообще была активность.
|
||||
|
||||
Почему для правильного LAST_VALUE важно задать рамку окна так, чтобы смотреть на всю историю пользователя, а не только до текущей строки?
|
||||
Потому что если не задавать рамку, то функция выдаст предыдущее значение от текущей строки.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
--task 5
|
||||
--Задача 5. Заказы и количество заказов пользователя (без GROUP BY)
|
||||
SELECT
|
||||
u.name AS user_name,
|
||||
o.id AS order_id,
|
||||
count(*) OVER (
|
||||
PARTITION BY u.name
|
||||
) AS orders_per_user
|
||||
FROM users u
|
||||
JOIN orders o ON o.user_id = u.id;
|
||||
|
||||
/*
|
||||
Чем такой результат отличается от классического отчёта «по пользователям» с GROUP BY user_id?
|
||||
GROUP BY уменьшил бы количество строк.
|
||||
*/
|
||||
|
||||
--task 6
|
||||
--Задача 6. Сравнение суммы заказа с предыдущим
|
||||
WITH revenue AS (
|
||||
SELECT
|
||||
o.id AS order_id,
|
||||
o.user_id AS user_id,
|
||||
o.order_date AS order_date,
|
||||
SUM(oi.quantity * oi.price_at_purchase) AS order_revenue
|
||||
FROM orders o
|
||||
JOIN order_items oi ON oi.order_id = o.id
|
||||
GROUP BY o.id, o.user_id, o.order_date
|
||||
)
|
||||
|
||||
SELECT
|
||||
u.name,
|
||||
order_id,
|
||||
order_date,
|
||||
order_revenue,
|
||||
LAG(order_revenue) OVER (PARTITION BY user_id ORDER BY order_date) AS prev_order_revenue
|
||||
FROM revenue r
|
||||
JOIN users u ON r.user_id = u.id;
|
||||
|
||||
/*
|
||||
Как по такому результату понять, увеличивается или уменьшается средний чек с течением времени?
|
||||
Можно дополнительно создать еще один столбец в котором будем получать разность текущего и предыдущего заказов, а на основе этого уже делать выводы.
|
||||
А по такому запросы - думаю наглядно видно в таблице какая сумма больше, а какая меньше.
|
||||
*/
|
||||
|
||||
--task 7
|
||||
--Задача 7. Категории с накопительной выручкой по времени
|
||||
WITH category_date_revenue AS (
|
||||
SELECT
|
||||
p.category,
|
||||
o.order_date::date AS order_date,
|
||||
SUM(oi.price_at_purchase * oi.quantity) AS daily_category_revenue
|
||||
FROM products p
|
||||
JOIN order_items oi
|
||||
ON p.id = oi.product_id
|
||||
JOIN orders o
|
||||
ON oi.order_id = o.id
|
||||
|
||||
GROUP BY
|
||||
p.category,
|
||||
o.order_date::date
|
||||
)
|
||||
|
||||
SELECT
|
||||
category AS category,
|
||||
order_date::date AS order_date,
|
||||
daily_category_revenue,
|
||||
SUM(daily_category_revenue) OVER (
|
||||
PARTITION BY category
|
||||
ORDER BY order_date
|
||||
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
) AS running_category_revenue
|
||||
FROM category_date_revenue;
|
||||
|
||||
|
||||
/*
|
||||
Чем такой «накопительный» график по категории отличается по смыслу от обычного ежедневного (без SUM OVER)?
|
||||
Видим какая именно категория в какой именно день принесла больше выручки
|
||||
*/
|
||||
Reference in New Issue
Block a user