homework, pet project DE Dmitry_Medvedev #1
@@ -0,0 +1,118 @@
|
||||
-- Задача 1
|
||||
SELECT u.name
|
||||
FROM users u
|
||||
WHERE id IN (
|
||||
SELECT o.user_id
|
||||
FROM orders o
|
||||
)
|
||||
ORDER BY u.name;
|
||||
-- Ответ на вопросы
|
||||
-- 1. JOIN склеивает одну таблицу со второй, а подзапрос с IN выполняет запрос и получает список с user_id,
|
||||
-- далее по этому списку отфильтровывает пользователей в основном запросе.
|
||||
-- 2. Когда запрос простой и короткий.
|
||||
|
||||
|
||||
-- Задача 2
|
||||
SELECT u.id,
|
||||
u.name,
|
||||
u.email
|
||||
FROM users u
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM orders o
|
||||
WHERE o.user_id = u.id
|
||||
);
|
||||
-- Ответ на вопросы
|
||||
-- 1. Предположу что NOT EXISTS более бещопасно работает с null.
|
||||
-- 2. Отправить таким пользователям рекомендацию, специальный промокод на скидку для первого заказа или рекламу.
|
||||
|
||||
|
||||
-- Задача 3
|
||||
SELECT u.name,
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM orders o
|
||||
WHERE u.id = o.user_id
|
||||
) AS orders_count
|
||||
FROM users u
|
||||
ORDER BY orders_count DESC, name;
|
||||
-- Ответ на вопросы
|
||||
-- 1. Если подзапрос SELECT вернётся две и более строки то будет ошибка.
|
||||
-- 2. Сложность возрастет и скорее всего нужно будет оптимизировать запрос.
|
||||
|
||||
|
||||
-- Задача 4
|
||||
WITH total_revenue_cte AS (
|
||||
SELECT o.user_id,
|
||||
SUM(oi.quantity * oi.price_at_purchase) AS total_price
|
||||
FROM orders o
|
||||
JOIN order_items oi ON oi.order_id = o.id
|
||||
GROUP BY o.user_id
|
||||
)
|
||||
SELECT u.name,
|
||||
trc.total_price AS total_revenue
|
||||
FROM users u
|
||||
JOIN total_revenue_cte trc ON trc.user_id = u.id
|
||||
ORDER BY total_revenue DESC;
|
||||
-- Ответ на вопросы
|
||||
-- 1. Проще читать запрос "посчитать выручку по пользователям" когда вынесен отдельно.
|
||||
-- 2. Вроде как в postgres можно в базу данных записывать функции, если я ничего не путаю.
|
||||
-- И далее вызывать эту функцию при необходимости, так можно избежать дублирования кода.
|
||||
|
||||
|
||||
-- Задача 5
|
||||
WITH product_price_at_purchase_cte AS (
|
||||
SELECT oi.product_id,
|
||||
SUM(oi.quantity * oi.price_at_purchase) AS product_revenue
|
||||
FROM order_items oi
|
||||
GROUP BY oi.product_id
|
||||
)
|
||||
SELECT p.name AS product_name,
|
||||
p.category,
|
||||
ppps.product_revenue
|
||||
FROM products p
|
||||
JOIN product_price_at_purchase_cte ppps ON ppps.product_id = p.id
|
||||
ORDER BY ppps.product_revenue DESC
|
||||
LIMIT 3;
|
||||
-- Ответ на вопросы
|
||||
-- 1. Возможно потому что products оcновная таблица, сналчала нужно из order_items достать агрегацию по id а потом склеивать с products.
|
||||
-- 2. Через CTE мы получаем готовую таблицу и далее делаем с ней что хотим,
|
||||
-- а в гуруппировке через GROUP BY нельзя добавить дополнительные поля name и category.
|
||||
|
||||
|
||||
-- Задача 6
|
||||
WITH total_revenue_cte AS (
|
||||
SELECT o.user_id,
|
||||
SUM(oi.quantity * oi.price_at_purchase) AS total_revenue
|
||||
FROM orders o
|
||||
JOIN order_items oi ON oi.order_id = o.id
|
||||
GROUP BY o.user_id
|
||||
)
|
||||
SELECT u.name,
|
||||
u.email,
|
||||
trc.total_revenue
|
||||
FROM users u
|
||||
JOIN total_revenue_cte trc ON trc.user_id = u.id
|
||||
WHERE u.is_active = TRUE AND trc.total_revenue > 1000
|
||||
ORDER BY total_revenue DESC;
|
||||
-- Ответ на вопросы
|
||||
-- 1. Думаю как то так - user_most_expenses
|
||||
-- 2. Можно предлагать рекламу товаров, анализировать что пользователи покупали и потом предлагать схожую продукцию.
|
||||
|
||||
|
||||
WITH order_revenue_cte AS (
|
||||
SELECT order_id,
|
||||
SUM(quantity * price_at_purchase) AS order_revenue
|
||||
FROM order_items
|
||||
GROUP BY order_id
|
||||
)
|
||||
SELECT o.id,
|
||||
o.order_date,
|
||||
oc.order_revenue
|
||||
FROM orders o
|
||||
JOIN order_revenue_cte oc ON oc.order_id = o.id
|
||||
WHERE o.status = 'paid' AND oc.order_revenue < 100;
|
||||
-- Ответ на вопросы
|
||||
-- 1. Какие реальные причины таких заказов, я думаю что просто не все люди богатые что бы заказывать на большие суммы (если я правильно понял статус paid)
|
||||
-- Чтобы узнать злоупотребления промокодами нужно копаться в других данных, где хранятся данные о введенных промокодах.
|
||||
-- 2. Я думаю выгоднее фильтровать внутри подзапроса, чтоб подзапрос не создавал большую таблицу и основному запросу не пришлось ее перебирать.
|
||||
@@ -0,0 +1,69 @@
|
||||
-- Задание 1
|
||||
CREATE MATERIALIZED VIEW my_category_revenue AS
|
||||
SELECT
|
||||
p.category,
|
||||
SUM(oi.quantity * oi.price_at_purchase) AS total_revenue
|
||||
FROM orders o
|
||||
JOIN order_items oi ON oi.order_id = o.id
|
||||
JOIN products p ON p.id = oi.product_id
|
||||
WHERE o.status = 'paid'
|
||||
GROUP BY p.category;
|
||||
|
||||
SELECT * FROM my_category_revenue
|
||||
ORDER BY total_revenue DESC;
|
||||
-- Ответ на вопросы
|
||||
-- 1. Не нужно писать длинные запросы и результат будет уже подсчитан.
|
||||
|
||||
|
||||
-- Задание 2
|
||||
CREATE MATERIALIZED VIEW mv_user_activity AS
|
||||
SELECT
|
||||
u.id AS user_id,
|
||||
u.name AS user_name,
|
||||
COUNT(o.user_id) AS orders_count
|
||||
FROM users u
|
||||
LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'paid'
|
||||
GROUP BY u.id, u.name;
|
||||
|
||||
SELECT * FROM mv_user_activity
|
||||
WHERE orders_count > 0
|
||||
ORDER BY orders_count DESC;
|
||||
-- Ответ на вопросы
|
||||
-- 1. Я так понял, что если критерий "самых лояльных" клиентов это колличество оплаченных заказов,
|
||||
-- то "самые лояльные" это те у кого этих заказов больше всего.
|
||||
-- 2. Покажет "самых лояльных" клиентов за последный месяц, кто-то из клиетов может сделать много заказов за 1-2 месяца, а кто то за год по чуть чуть заказывает.
|
||||
-- Возможно еще какие то из клиентов ориентируются на "сезонные" товары и у них будет сплеск продажи за последний месяц, или кто то из клиентов покупал много за год, а в этом месяце перестал делать заказы.
|
||||
|
||||
|
||||
-- Задание 3
|
||||
CREATE MATERIALIZED VIEW mv_daily_revenue AS
|
||||
SELECT
|
||||
o.order_date::date AS sales_date,
|
||||
SUM(oi.quantity * price_at_purchase) AS daily_revenue
|
||||
FROM orders o
|
||||
JOIN order_items oi ON oi.order_id = o.id
|
||||
WHERE o.status = 'paid'
|
||||
GROUP BY o.order_date::date;
|
||||
|
||||
SELECT * FROM mv_daily_revenue
|
||||
ORDER BY sales_date DESC;
|
||||
-- Ответ на вопросы
|
||||
-- 1. «Просадки» и «пики» будут видны в колонке daily_revenue
|
||||
-- 2. Быстрее считывать с готовой витрины mv_daily_revenuе, на лету из сырых таблиц дольше будет обрабатываться запрос.
|
||||
|
||||
|
||||
-- Задание 4
|
||||
REFRESH MATERIALIZED VIEW mv_daily_revenue;
|
||||
-- Расписание интернет‑магазина с несколькими заказами в день:
|
||||
-- Для такого сценария подойдет обновление витрины раз в неделю по ночам.
|
||||
-- Так как заказов не так уж и много, можно обновлять статискиту чаще.
|
||||
-- Ночью, потому что заказов будет меньше в то время суток и бд быстрее обработает ззапрос.
|
||||
|
||||
-- Расписание крупного магазина с тысячами заказов в час:
|
||||
-- Для такого сценария подойдет обновление витрин раз в месяц - раз в квартал по ночам,
|
||||
-- Так как данных много и частые расчеты будут сильно нагружать базу данных.
|
||||
-- Возможно, нужно будет получить данные аналитикам раньше указанных сроков.
|
||||
|
||||
-- Ответ на вопросы
|
||||
-- 1. Данные будут устаревшими, из за того результат работы аналитиков будут не достоверными.
|
||||
-- 2. Слишком частые обновления витрит будут сильно нагружать БД.
|
||||
Reference in New Issue
Block a user