diff --git a/aharlamcev/postgresql/SQL2.0FULL.sql b/aharlamcev/postgresql/SQL2.0FULL.sql new file mode 100644 index 0000000..7cc305b --- /dev/null +++ b/aharlamcev/postgresql/SQL2.0FULL.sql @@ -0,0 +1,523 @@ +----------------------------------------------------------- +--DDL +create table users ( +id INT primary key not null, +name VARCHAR(100), +age INT, +email VARCHAR(100), +country VARCHAR(50), +is_active BOOLEAN, +created_at DATE +); + +create table products ( +id INT primary key, +name VARCHAR(100) not null, +category VARCHAR(50), +price DECIMAL(10, 2) not null, +in_stock BOOLEAN +); + +create table orders ( +id INT primary key, +user_id INT, +order_date DATE not null, +status VARCHAR(20) +); + +create table order_items ( +order_id INT, +product_id INT, +quantity INT not null, +price_at_purchase DECIMAL(10, 2) not null, +primary key (order_id, product_id) +); + +alter table users +alter column created_at type timestamptz; + +-------------------------------------------------------------------------------------- +-- DML +INSERT INTO users (id, name, age, email, country, is_active, created_at) +VALUES +(1, 'Alice', 25, 'alice@example.com', 'US', true, '2024-01-10 10:00:00'), +(2, 'Bob', 30, 'bob@example.com', 'UK', true, '2024-02-05 09:30:00'), +(3, 'Carol', 27, 'carol@example.com', 'US', false, '2024-03-01 15:20:00'), +(4, 'Diana', 22, 'diana@GMAIL.com', 'US', true, '2024-01-12 08:15:00'), +(5, 'Evan', 35, 'EVAN@example.com', 'UK', true, '2024-01-18 19:40:00'), +(6, 'Farida', 29, 'farida@example.com', 'KZ', true, '2024-02-20 11:05:00'), +(7, 'George', 31, 'george+test@example.com', 'US', true, '2024-03-05 16:45:00'), +(8, 'Helen', 24, NULL, 'UK', false, '2024-03-06 08:20:00'), +(9, 'Ivan', 26, 'ivan@example.com', 'RU', true, '2024-01-11 14:30:00'), +(10, 'Julia', 28, 'julia@example.com', 'US', true, '2024-03-07 18:05:00'); + + +INSERT INTO products (id, name, category, price, in_stock) +VALUES +(1, 'iPhone 15', 'Электроника', 80000, true), +(2, 'Kindle', 'Электроника', 15000, true), +(3, 'Кроссовки Nike', 'Одежда', 9000, true), +(4, 'Кроссовки NoName', 'Одежда', 3000, false), +(5, 'Наушники Pro', 'Электроника', 12000, true), +(6, 'Фитнес‑браслет', 'Гаджеты', 6000, true), +(7, 'Подарочная карта', 'Сервисы', 1000, true); + + +INSERT INTO orders (id, user_id, order_date, status) +VALUES +(1, 1, '2025-01-10 12:00:00', 'paid'), +(2, 3, '2025-01-12 09:30:00', 'cancelled'), +(3, 1, '2025-01-15 18:20:00', 'paid'), +(4, 2, '2025-01-20 11:05:00', 'created'), +(5, 4, '2025-02-01 17:45:00', 'paid'), +(6, 5, '2025-02-10 10:10:00', 'refunded'), +(7, 7, '2025-03-01 09:00:00', 'paid'), +(8, 9, '2025-03-05 19:30:00', 'paid'); + + +INSERT INTO order_items (order_id, product_id, quantity, price_at_purchase) +VALUES +(1, 1, 1, 78000), +(1, 3, 2, 8500), +(2, 2, 1, 14000), +(3, 5, 1, 11000), +(3, 7, 1, 1000), +(4, 2, 1, 15000), +(5, 6, 1, 6000), +(6, 1, 1, 82000), +(7, 3, 1, 9000), +(8, 4, 2, 2800); + + +-------------------------------------------------------------------------------------------- +--BASE SQL +select id, name, country, created_at, email from users +order by created_at desc, name; + +select id, name, country from users +where country = 'US' -- 'UK' +order by name; + +select id, name, email from users +where email is not null +order by name; + +select distinct LOWER(email) as email_lower from users +where email is not null; + +select id, name, created_at from users +where created_at > '2024-01-11' +order by created_at desc +limit 2 offset 1; +------------------------------------------------------------------------------------------------------ +-- FILTRATION +select name, category, price, +price * 1.3 as price_with_markup, +price * 1.5 as price_with_vat from products +where in_stock is true +and category not in ('Гаджеты') +and price >= 100 and price <=1300; + +select name, price, +price * 0.85 as price_with_discount +from products +where price < 100 or price > 1000; + +select id, name, category, price from products +where category in ('Электроника') and price >= 15000; + +select name, category, price from products +where name not like '%iPhone%'; + +select * from products +where name ilike '%pro%'; + +--------------------------------------------------------------------------------------------------- +-- JOIN +-- 1 задание +select o.id as order_id, order_date, status, u.name as user_name, country from orders o +join users u on o.user_id = u.id; + +-- 2 задание + +select o.id as order_id, p.name as product_name, oi.quantity, oi.price_at_purchase from orders o +join order_items oi on o.id = oi.order_id +join products p on oi.product_id = p.id +where o.id = 1; + +-- 3 задание +select o.id as order_id, p.name as product_name, oi.quantity, oi.price_at_purchase from orders o +join order_items oi on o.id = oi.order_id +join products p on oi.product_id = p.id; + +-- 4 задание +select u.name as user_name, o.id as order_id, o.status from users u +left join orders o on u.id = o.user_id; + +-- 5 задание +select u.id as user_id, u.name as user_name from users u +left join orders o on u.id = o.user_id +where o.user_id is null; + +-- 6 задание + +select o.id as order_id, p.name as product_name, oi.quantity, oi.price_at_purchase from orders o +join order_items oi on o.id = oi.order_id +join products p on oi.product_id = p.id +where p.name = 'Фитнес‑браслет'; + +-- 7 задание + +select o.id as order_id, p.name as product_name, oi.quantity, oi.price_at_purchase from orders o +join order_items oi on o.id = oi.order_id +join products p on oi.product_id = p.id +where o.status = 'paid'; + +-- 8 задание + +select o.id, o.order_date, p.name as product_name, oi.quantity, oi.price_at_purchase from orders o +join order_items oi on o.id = oi.order_id +join products p on oi.product_id = p.id +where o.user_id in (select id from users where name = 'Alice'); + +-- 9 задание + +select o.id as order_id, o.user_id, o.status from orders o +left join order_items oi on o.id = oi.order_id +where oi.product_id is null; -- таких у нас заказов нет, во всех заказах есть товары. + +-------------------------------------------------------------------------------------------- +-- UNION +-- 1 задание +select id, name, is_active from users +where is_active is false +union +select id, name, is_active from users +where is_active is true +order by name; + +-- 2 задание +select id as order_id, user_id, order_date, +case when o.order_date < '2025-02-01' then 'old' else 'new' end as period +from orders o; +-- 3е задание не имеет смысла, т.к. все заказы покупались, там нечего соединять + +---------------------------------------------------------------------------------------------- +-- Agregation +-- 1 задание +select category, COUNT(*) as products_count from products +group by category +order by products_count desc; + +-- 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; + +-- 3 задание +select p.category, SUM(oi.quantity * oi.price_at_purchase) as category_revenue from products p +join order_items oi on oi.product_id = p.id +group by p.category +order by category_revenue desc; + +-- 4 задание +select order_id, SUM(quantity * price_at_purchase) as order_revenue from order_items oi +join orders o on oi.order_id = o.id +where o.status = 'paid' +group by oi.order_id +order by order_revenue desc; + +-- 5 задание +select p.category, SUM(oi.quantity * oi.price_at_purchase) as category_revenue from products p +join order_items oi on oi.product_id = p.id +group by p.category +having SUM(oi.quantity * oi.price_at_purchase) > 1000 +order by category_revenue desc; + +-- 6 задание +select country, COUNT(*) as active_users_count +from users +where is_active is true +group by country +having COUNT(*) >= 2 +order by active_users_count desc; + +-- 7 задание +select p.name, SUM(oi.quantity) as total_quantity_sold from products p +join order_items oi on oi.product_id = p.id +group by p.name +order by total_quantity_sold desc +limit 3; + +-- 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) < 10000 -- сделал меньше 10000 потому что заказов дешевле 100 нет. +order by order_revenue; + +-------------------------------------------------------------------------------------------------------------- +--Functions:date strings logic +-- 1 задание +select name, +created_at::date as created_date, +CURRENT_DATE - created_at::date as days_since_registration +from users +where CURRENT_DATE - created_at::date > 750 +order by days_since_registration desc; + +-- 2 задание +select name, LOWER(email) as email_clean, +substring(LOWER(email) from position('@' in email) + 1) as domain +from users +where email is not null +order by domain, name; + +-- 3 задание +select name, age, +case when age < 25 then 'young' + when age between 20 and 30 then 'adult' + else 'senior' + end as age_group +from users +order by age; + +-- 4 задание +with CTE as + (select name, created_at::date, + current_date - created_at::date as days_since_registration, + case when is_active = true then 'hot' + when current_date - created_at::date < 30 then 'hot' + else 'regular' + end as hot_flag + from users) +select * from CTE +where hot_flag = 'hot'; + +-- 5 задание +with CTE as + (select count(id) as total_users, + COUNT(email) as with_email, + (select count(*) from users where email is null) as without_email + from users) +select total_users, with_email, without_email, + ROUND(100.0 * without_email / total_users, 2) as percentage_no_email + from CTE; + + -- 6 задание + select name, + CURRENT_DATE - created_at::date as days_since_registration, + case + when CURRENT_DATE - created_at::date <= 720 then 'new' + when CURRENT_DATE - created_at::date BETWEEN 721 and 750 then 'middle' + else 'old' + end as tenure_group + from users + order by days_since_registration; + + ---------------------------------------------------------------------------------------------------- + --Subquery/CTE + -- 1 задание +select name +from users +where id in (select distinct user_id from orders) +order by name; + +-- 2 задание +select id, name, email +from users u +where not exists ( +select user_id +from orders o +where o.user_id = u.id) + +-- 3 задание +select name, +(select COUNT(*) orders_count from orders o +where o.user_id = u.id) +from users u +order by orders_count desc, name; + +-- 4 задание +with CTE as ( + select user_id, SUM(quantity * price_at_purchase) as total_revenue + from orders o + join order_items oi on o.id = oi.order_id + group by user_id + ) +select u.name, c.total_revenue from users u +join CTE c on u.id = c.user_id +order by c.total_revenue desc; + +-- 5 задание +with CTE as ( + select product_id, SUM(quantity * price_at_purchase) as product_revenue + from order_items + group by product_id + ) +select p.name as product_name, p.category, c.product_revenue from CTE c +join products p on c.product_id = p.id +order by c.product_revenue desc +limit 3; + +-- 6 задание +with CTE as ( + select o.user_id, SUM(quantity * price_at_purchase) as total_revenue from orders o + join order_items oi on o.id = oi.order_id + group by o.user_id + ) +select u.name, u.email, c.total_revenue from users u +join CTE c on u.id = c.user_id +where u.is_active is true +and c.total_revenue > 10000; + +-- 7 задание + +with CTE as ( + select order_id, SUM(quantity * price_at_purchase) as order_revenue + from order_items oi + join orders o on oi.order_id = o.id + where o.status = 'paid' + group by order_id + ) +select * from CTE +where order_revenue < 10000; + +--------------------------------------------------------------------------------------------- +--WINDOW FUNCTIONS +-- 1 задание +select u.id as user_id, u.name, o.id as order_id, o.order_date, +row_number() over (partition by o.user_id order by o.order_date) as order_number_for_user +from users u +join orders o on u.id = o.user_id; + +-- 2 задание +with CTE as ( + select u.id as user_id, u.name as user_name, order_id, o.order_date, SUM(quantity * price_at_purchase) as order_revenue + from order_items oi + join orders o on oi.order_id = o.id + join users u on o.user_id = u.id + group by u.id, u.name, order_id, o.order_date + order by user_id + ) +select user_id, user_name, order_id, order_date, order_revenue, +SUM(order_revenue) over (partition by user_id order by order_date) as running_revenue_for_user +from CTE + +-- 3 задание +with CTE as ( + select u.id as user_id, u.name as user_name, SUM(quantity * price_at_purchase) as total_revenue + from order_items oi + join orders o on oi.order_id = o.id + join users u on o.user_id = u.id + group by u.id, u.name + ) +select user_name, total_revenue, +RANK() over (order by total_revenue desc) as revenue_rank, +dense_rank() over (order by total_revenue desc) as dense_revenue_rank +from CTE; + + +-- 4 задание +select u.name, o.id as order_id, order_date, +first_value(o.order_date) over (partition by user_id order by order_date) as first_order_date, +last_value(o.order_date) over (partition by user_id order by 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; + + +-- 5 задание +select u.name as user_name, o.id as order_id, +COUNT(*) over (partition by u.id) as orders_per_user +from users u +join orders o on u.id = o.user_id; + +-- 6 задание +with CTE as ( + select u.id as user_id, u.name as user_name, order_id, o.order_date as order_date, + SUM(quantity * price_at_purchase) as order_revenue + from order_items oi + join orders o on oi.order_id = o.id + join users u on o.user_id = u.id + group by u.id, u.name, order_id, o.order_date + order by user_id + ) +select user_name, order_id, order_date, order_revenue, +LAG(order_revenue) over (partition by user_id order by order_date) as prev_order_revenue +from CTE; + +-- 7 задание +with CTEa as ( + select product_id, + o.order_date::date as order_date, + SUM(quantity * price_at_purchase) as daily_category_revenue + from order_items oi + join orders o on oi.order_id = o.id + group by product_id, order_date + order by oi.product_id + ), + CTEb as ( + select p.category, order_date, daily_category_revenue + from CTEa a + join products p on a.product_id = p.id + ) +select category, order_date, daily_category_revenue, +SUM(daily_category_revenue) over (partition by category order by order_date) as running_category_revenue +from CTEb; + +----------------------------------------------------------------------------------------------------------- +-- VIEW and MATERIALIZED VIEW +-- 1 задание +create MATERIALIZED view mv_category_revenue +as + select p.category, SUM(quantity * price_at_purchase) as total_revenue + from products p + join order_items oi on oi.product_id = p.id + join orders o on oi.order_id = o.id and o.status = 'paid' + group by p.category + order by total_revenue desc +; + +-- 2 задание +create materialized view +as + select u.id, u.name, COUNT(o.id) as order_count + from users u + join orders o on u.id = o.user_id and o.status = 'paid' + group by u.id, u.name + order by order_count desc +; + +-- 3 задание +create materialized view mv_daily_revenue +as + select order_date::date as sales_date, SUM(quantity * price_at_purchase) as daily_revenue from orders o + join order_items oi on o.id = oi.order_id + where o.status = 'paid' + group by sales_date + order by sales_date +; + +-- 4 задание +refresh materialized view mv_daily_revenue; + +-- В 1 случае витрину можно обновлять 1 раз в день в 10:00 или 2 раза в день в 10:00 и в 22:00 +-- Во 2 случае витрину нужно обновлять несколько раз в день, думаю каждые часа 2-3 то есть в 8 в 10 в 12 и тд. + + + + + + + + + + + + + + + + +