From 4b96e6012fe6d877b49d3d290b04ad5244ff7db9 Mon Sep 17 00:00:00 2001 From: mnoskov Date: Wed, 11 Mar 2026 13:40:31 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=BA=D1=82=D0=B8=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=9E=D0=BA=D0=BE=D0=BD=D0=BD=D1=8B=D0=B5=20=D1=84?= =?UTF-8?q?=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DQL_window_functions_practice.sql | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 mnoskov/postgresql/7. Оконные функции/DQL_window_functions_practice.sql diff --git a/mnoskov/postgresql/7. Оконные функции/DQL_window_functions_practice.sql b/mnoskov/postgresql/7. Оконные функции/DQL_window_functions_practice.sql new file mode 100644 index 0000000..4d582be --- /dev/null +++ b/mnoskov/postgresql/7. Оконные функции/DQL_window_functions_practice.sql @@ -0,0 +1,168 @@ +-- Задание 1 +SELECT + o.user_id, + u.name AS user_name, + o.id AS order_id, + o.order_date::date, + row_number() OVER (PARTITION BY o.user_id ORDER BY o.order_date) AS order_number_for_user +FROM + mnoskov.orders o +JOIN + mnoskov.users u ON o.user_id = u.id; +-- Отличае от обычного order by в том, что мы не просто сортируем, а сортируем в пределах одного пользователя, и ещё выводим нумерацию заказов + + +-- Задание 2 +WITH order_total_revenue AS ( + SELECT + o.user_id, + oi.order_id, + o.order_date::date, + sum(oi.quantity * oi.price_at_purchase) AS order_revenue + FROM + mnoskov.order_items oi + JOIN + mnoskov.orders o ON oi.order_id = o.id + GROUP BY + o.user_id, oi.order_id, o.order_date +) +SELECT + otr.user_id, + u.name AS user_name, + otr.order_id, + otr.order_date::date, + otr.order_revenue, + sum(otr.order_revenue) OVER (PARTITION BY otr.user_id ORDER BY otr.order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_revenue_for_user +FROM + mnoskov.users u +JOIN + order_total_revenue otr ON u.id = otr.user_id; +-- Если не задвать рамку, то могут пояиться проблемы с дубликатами +-- Если накопительная выручка пользователя становится больше, значит он начинает тратить больше + + +-- Задание 3 +WITH user_total_revenue 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 AS user_name, + utr.total_revenue, + rank() OVER (ORDER BY utr.total_revenue) AS revenue_rank, + dense_rank() OVER (ORDER BY utr.total_revenue) AS revenue_dense_rank +FROM + mnoskov.users u +JOIN + user_total_revenue utr ON u.id = utr.user_id; +-- Когда появляется одинаковая выручка, dense_rank нумерует по порядку, без пропусков(1,2,2,3), а rank по 'расположению'(1,2,2,4) + + +-- Задание 4 +SELECT + u.name AS user_name, + o.id AS order_id, + o.order_date::date, + first_value(o.order_date::date) OVER (PARTITION BY u.id ORDER BY o.order_date) AS first_order_date, + last_value(o.order_date::date) OVER (PARTITION BY u.id ORDER BY o.order_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_order_date +FROM + mnoskov.orders o +JOIN + mnoskov.users u ON o.user_id = u.id; +-- Первый заказ можно использовать для отчета 'как давно клиент начал активничать', а последний заказ, например если давно был последний заказ, то кинуть рассылку с промокодом, чтобы клиент снова начала заказывать как вариант +-- Если не задать рамку, то дата последнего заказа будет равна текущей строке, а не последней + + +-- Задание 5 +SELECT + u.name AS user_name, + o.id AS order_id, + count(*) OVER (PARTITION BY u.id) AS orders_per_user +FROM + mnoskov.orders o +JOIN + mnoskov.users u ON o.user_id = u.id; +-- Если использовать просто group by, то пользователи сожмутся в одну ячейку + + +-- Задание 6 +WITH order_total_revenue AS ( + SELECT + o.user_id, + oi.order_id, + o.order_date::date, + sum(oi.quantity * oi.price_at_purchase) AS order_revenue + FROM + mnoskov.orders o + JOIN + mnoskov.order_items oi ON o.id = oi.order_id + GROUP BY + o.user_id, oi.order_id, o.order_date +) +SELECT + u.name AS user_name, + otr.order_id, + otr.order_date::date, + otr.order_revenue, + lag(otr.order_revenue) OVER (PARTITION BY u.id ORDER BY otr.order_date) AS prev_order_revenue +FROM + mnoskov.users u +JOIN + order_total_revenue otr ON u.id = otr.user_id; +-- если текущее значение среднего чека больше чем предидущие, то увеличивается, а если наоборот, то уменьшается + + +-- Задание 7 +-- По месяцам: +WITH category_total_revenue AS ( + SELECT + p.category, + date_trunc('month', o.order_date)::date AS order_date_month, + sum(oi.quantity * oi.price_at_purchase) AS monthly_category_revenue + FROM + mnoskov.products p + JOIN + mnoskov.order_items oi ON p.id = oi.product_id + JOIN + mnoskov.orders o ON oi.order_id = o.id + GROUP BY + p.category, order_date_month +) +SELECT + category, + order_date_month, + monthly_category_revenue, + sum(monthly_category_revenue) OVER (PARTITION BY category ORDER BY order_date_month ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_category_revenue +FROM + category_total_revenue; + +-- По дням: +WITH category_total_revenue AS ( + SELECT + p.category, + o.order_date::date AS order_date, + sum(oi.quantity * oi.price_at_purchase) AS daily_category_revenue + FROM + mnoskov.products p + JOIN + mnoskov.order_items oi ON p.id = oi.product_id + JOIN + mnoskov.orders o ON oi.order_id = o.id + GROUP BY + p.category, order_date +) +SELECT + category, + 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_total_revenue; +-- По такому графику мы можем видеть РОСТ выручки по дням например, а не просто сколько в какой день принесла категория \ No newline at end of file