----------------------------------------------------------- --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) FOREIGN KEY (user_id) REFERENCES users(id) ); 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) FOREIGN KEY () ); 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 < 20 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 и тд.