All homework SQL2.0 done in one file

This commit is contained in:
Alex
2026-02-15 15:30:39 +03:00
parent df8159a287
commit 4aa11c0136
+523
View File
@@ -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 и тд.