From 4b403441c8df90363da952ea93f75491f1ffe9ae Mon Sep 17 00:00:00 2001 From: "valera.beretar" Date: Sat, 23 May 2026 00:42:45 +0300 Subject: [PATCH 1/2] Add homework on SQL aggregation and grouping --- .../postgresql/sql_aggregation_grouping.sql | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 vberetar/postgresql/sql_aggregation_grouping.sql diff --git a/vberetar/postgresql/sql_aggregation_grouping.sql b/vberetar/postgresql/sql_aggregation_grouping.sql new file mode 100644 index 0000000..6140687 --- /dev/null +++ b/vberetar/postgresql/sql_aggregation_grouping.sql @@ -0,0 +1,105 @@ +-- Задание 1 +SELECT category, COUNT(*) AS products_count +FROM products +GROUP BY category +ORDER BY products_count DESC; + +-- Ответ на вопросы +-- 1. С ORDER BY гораздо удобней читать результат, без него данные отдаются в непонятном порядке. +-- 2. Для категории smartphone добавится +1 значение. + + +-- Задание 2 +SELECT order_id, + SUM(quantity) AS total_quantity, + SUM(quantity * price_at_purchase) AS order_revenue +FROM order_items +GROUP BY order_id +ORDER BY order_revenue DESC; + +-- Ответ на вопросы +-- 1. Если убрать GROUP BY и order_id то запрос выдаст 1 строку со всеми суммами total_quantity и order_revenue. +-- Если убрать только GROUP BY и не убрать order_id то выдаст ошибку. Так как нельзя оставлять неагрегированные столбцы без GROUP BY. +-- 2. Если добавить product_id без добавления её в GROUP BY или агрегат то выдаст ошибку. + + +-- Задание 3 +SELECT p.category, + SUM(oi.quantity * oi.price_at_purchase) AS category_revenue +FROM order_items oi +JOIN products p ON p.id = oi.product_id +GROUP BY p.category +ORDER BY category_revenue; + +-- Ответ на вопросы +-- 1. По задаче нам же нужно сгруппировать и посчитать сумму по категориям а не по id товарам. +-- 2. Если добавить p.name в SELECT без добавления её в GROUP BY или агрегат то выдаст ошибку. Нельзя вставлять неагрегированные столбцы без GROUP BY. + + +-- Задание 4 +SELECT oi.order_id, + SUM(oi.quantity * oi.price_at_purchase) AS order_revenue +FROM orders o +JOIN order_items oi ON oi.order_id = o.id +WHERE o.status = 'paid' +GROUP BY oi.order_id +ORDER BY order_revenue DESC; + +-- Ответ на вопросы +-- 1. В этом задании мы получили сумму(выручку) по заказам оплаченным, а в прошлом задании мы получили сумму(выручку) по категориям товаров. +-- 2. Посчитать выручку, сколько заработала компания. Остальные заказы еще не оплачены. + + +-- Задание 5 +SELECT p.category, + SUM(oi.quantity * oi.price_at_purchase) AS category_revenue +FROM order_items oi +JOIN products p ON p.id = oi.product_id +GROUP BY p.category +HAVING SUM(oi.quantity * oi.price_at_purchase) > 1000 +ORDER BY category_revenue; + +-- Ответ на вопросы +-- 1. WHERE не принимает агрегирующие функции, можно сделать так - WHERE oi.quantity * oi.price_at_purchase > 1000 +-- 2. WHERE фильтрует до GROUP BY, а HAVING после GROUP BY. + + +-- Задание 6 +SELECT country, + COUNT(*) AS active_users_count +FROM users +WHERE is_active = TRUE +GROUP BY country +HAVING COUNT(*) > 2 +ORDER BY active_users_count DESC; + +-- Ответ на вопросы +-- 1. Предположу что отбрасывает неактивных пользователей сразу, перед групировкой и агрегаций. В дальнейшем может сэкономить на времени вычисления, особенно если данных будет много. +-- 2. Пустую таблицу без строк. + + +-- Задание 7 +SELECT p.name AS product_name, + SUM(oi.quantity) AS total_quantity_sold +FROM order_items oi +JOIN products p ON p.id = oi.product_id +GROUP BY product_name +ORDER BY total_quantity_sold DESC +LIMIT 3; + +-- Ответ на вопросы +-- 1. Тут мы выясняем какой товар самый ходовой и сколько его продается, а рейтинг по выручке показывает сколько зарабатывает фирма от продажи товара. +-- 2. В случаях когда, например, нужно выяснить какой товар хорошо продается чтоб его закупить на склад для продажи. + + +-- Задание 8 +SELECT order_id, + SUM(quantity * price_at_purchase) AS order_revenue +FROM order_items +GROUP BY order_id +HAVING SUM(quantity * price_at_purchase) < 100 +ORDER BY order_revenue DESC; + +-- Ответ на вопросы +-- 1. Есть товары дешевые и люди которые мало зарабатывают, ещё есть всякие скидки на ряд товаров. +-- 2. Я думаю проще увидеть отфильтрованные заказы, чем листать весь список с заказами. From f8c0af41cae885e75fdf1acae485fd0f1251e717 Mon Sep 17 00:00:00 2001 From: "valera.beretar" Date: Wed, 27 May 2026 10:37:52 +0300 Subject: [PATCH 2/2] Add homework on SQL CTE --- vberetar/postgresql/sql_cte.sql | 118 ++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 vberetar/postgresql/sql_cte.sql diff --git a/vberetar/postgresql/sql_cte.sql b/vberetar/postgresql/sql_cte.sql new file mode 100644 index 0000000..080d9bc --- /dev/null +++ b/vberetar/postgresql/sql_cte.sql @@ -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. Я думаю выгоднее фильтровать внутри подзапроса, чтоб подзапрос не создавал большую таблицу и основному запросу не пришлось ее перебирать.