Merge branch 'main' into OS-418

This commit is contained in:
2026-04-10 22:20:11 +04:00
6 changed files with 1047 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
CREATE MATERIALIZED VIEW platform_summary AS
with users_counts as (
select
COUNT(distinct u.id) as total_users,
COUNT(distinct lv.user_id) as users_with_views
from users u
left join lesson_views lv on lv.user_id = u.id
),
courses_lessons_count as (
select COUNT(distinct c.id) as total_courses,
COUNT(distinct l.id) as total_lessons
from courses c
left join lessons l on l.course_id = c.id
),
views_count as (
select COUNT(distinct lv.lesson_id) as lessons_with_views,
COUNT(distinct lv.id) as total_views
from lesson_views lv
)
select uc.total_users, clc.total_courses, clc.total_lessons, uc.users_with_views,
ROUND(1.00 * clc.total_lessons / nullif(clc.total_courses, 0), 2) as avg_lessons_per_course,
ROUND(1.00 * vc.total_views / nullif(clc.total_lessons, 0), 2) as avg_views_per_lesson
from users_counts uc
cross join courses_lessons_count clc
cross join views_count vc
;
+28
View File
@@ -0,0 +1,28 @@
CREATE MATERIALIZED VIEW users_cohort_analysis AS
with needed_users as (
select distinct u.id as user_id,
u.registration_date as reg_date, e.course_id, lv.id as view_id,
lv.lesson_id as viewed_lesson_id,
FIRST_VALUE(lv.viewed_at) over (partition by lv.user_id order by lv.viewed_at) as first_view_date
from users u
left join enrollments e on u.id = e.user_id
left join lesson_views lv on e.user_id = lv.user_id
order by user_id
),
CTE as (
select
TO_CHAR(reg_date, 'YYYY-MM') as cohort_month,
COUNT(distinct user_id) as users_in_cohort,
COUNT(distinct case when user_id in (select user_id from enrollments) then user_id end) as enrolled_users,
COUNT(distinct case when user_id in (select user_id from lesson_views) then user_id end) as active_users,
COUNT(course_id) as courses_count,
extract('days' from AVG(first_view_date - reg_date)) as avg_time_to_first_view
from needed_users
group by cohort_month
)
select cohort_month, users_in_cohort, enrolled_users, active_users,
ROUND(100.0 * active_users / users_in_cohort, 2) as retention_rate,
courses_count::numeric / users_in_cohort as avg_courses_per_user,
avg_time_to_first_view
from CTE
;
+466
View File
@@ -0,0 +1,466 @@
create table users (
id INT primary key,
name VARCHAR(50) NOT NULL,
age INT,
email VARCHAR(50) NOT NULL,
registration_date date
);
INSERT INTO users (id, name, age, email, registration_date)
VALUES
(1, 'Alice', 25, 'alice@mail.com', '2023-01-10'),
(2, 'Bob', 30, 'bob@gmail.com', '2023-02-05'),
(3, 'Charlie', 22, 'charlie@mail.com', '2023-02-20'),
(4, 'Diana', 28, 'diana@mail.com', '2023-03-01'),
(5, 'Ethan', 35, 'ethan@gmail.com', '2023-03-10'),
(6, 'Frank', 29, 'frank@mail.com', '2023-01-05'),
(7, 'Grace', 26, 'grace@gmail.com', '2023-01-12'),
(8, 'Henry', 32, 'henry@mail.com', '2023-01-20'),
(9, 'Iris', 24, 'iris@gmail.com', '2023-02-10'),
(10, 'Jack', 27, 'jack@mail.com', '2023-04-15'),
(11, 'Kelly', 23, 'kelly@gmail.com', '2023-04-20'),
(12, 'Leo', 31, 'leo@mail.com', '2023-01-15'),
(13, 'Mia', 26, 'mia@gmail.com', '2023-01-18'),
(14, 'Nina', 28, 'nina@mail.com', '2023-01-08'),
(15, 'Oscar', 33, 'oscar@gmail.com', '2023-01-10'),
(16, 'Paul', 29, 'paul@mail.com', '2023-02-15'),
(17, 'Quinn', 24, 'quinn@gmail.com', '2023-03-20'),
(18, 'Rita', 27, 'rita@mail.com', '2023-04-01'),
(19, 'Sam', 30, 'sam@gmail.com', '2023-04-10'),
(20, 'Tina', 25, 'tina@mail.com', '2023-04-18');
create table courses (
id INT primary key,
title VARCHAR(100),
category VARCHAR(50),
created_at date
);
INSERT INTO courses (id, title, category, created_at)
VALUES
(1, 'SQL для начинающих', 'data', '2023-01-01'),
(2, 'Python для анализа данных', 'programming', '2023-01-15'),
(3, 'BI с нуля', 'business', '2023-03-01'),
(4, 'Excel для аналитиков', 'business', '2023-02-01'),
(5, 'Основы машинного обучения', 'data', '2023-03-15');
create table lessons (
id INT primary key,
course_id INT,
title VARCHAR(100),
duration_min INT,
foreign key (course_id) references courses(id)
);
INSERT INTO lessons (id, course_id, title, duration_min)
VALUES
(1, 1, 'SELECT и FROM', 10),
(2, 1, 'JOIN', 15),
(3, 1, 'WHERE и фильтрация', 12),
(4, 1, 'Агрегации и GROUP BY', 18),
(5, 1, 'Подзапросы', 25),
(6, 2, 'Pandas основы', 20),
(7, 2, 'NumPy', 15),
(8, 2, 'Визуализация данных', 22),
(9, 3, 'Основы BI', 12),
(10, 3, 'Метрики и дашборды', 18),
(11, 3, 'Power BI введение', 20),
(12, 4, 'Формулы в Excel', 15),
(13, 4, 'Сводные таблицы', 18),
(14, 4, 'Графики и диаграммы', 14),
(15, 5, 'Введение в ML', 20),
(16, 5, 'Линейная регрессия', 30),
(17, 5, 'Классификация', 28),
(18, 5, 'Кластеризация', 25);
create table enrollments (
id INT primary key,
user_id INT,
course_id INT,
enrolled_at date,
foreign key (user_id) references users(id),
foreign key (course_id) references courses(id)
);
INSERT INTO enrollments (id, user_id, course_id, enrolled_at)
VALUES
(1, 1, 1, '2023-01-15'),
(2, 1, 2, '2023-02-01'),
(3, 2, 1, '2023-01-20'),
(4, 3, 2, '2023-03-05'),
(5, 4, 3, '2023-04-01'),
(6, 6, 1, '2023-01-06'),
(7, 6, 2, '2023-01-10'),
(8, 6, 4, '2023-02-05'),
(9, 7, 1, '2023-01-13'),
(10, 7, 3, '2023-03-10'),
(11, 8, 1, '2023-01-22'),
(12, 8, 2, '2023-01-22'),
(13, 8, 3, '2023-01-22'),
(14, 9, 4, '2023-02-12'),
(15, 10, 1, '2023-04-16'),
(16, 11, 2, '2023-04-21'),
(17, 12, 1, '2023-01-16'),
(18, 13, 2, '2023-01-19'),
(19, 13, 4, '2023-02-20'),
(20, 14, 1, '2023-01-09'),
(21, 14, 2, '2023-01-09'),
(22, 14, 3, '2023-01-09'),
(23, 14, 4, '2023-02-01'),
(24, 14, 5, '2023-03-20'),
(25, 15, 1, '2023-01-11'),
(26, 15, 3, '2023-03-05'),
(27, 16, 2, '2023-02-16'),
(28, 16, 4, '2023-02-16'),
(29, 17, 3, '2023-03-22'),
(30, 17, 5, '2023-03-25'),
(31, 18, 1, '2023-04-02'),
(32, 19, 1, '2023-04-11'),
(33, 19, 2, '2023-04-11'),
(34, 20, 2, '2023-04-19'),
(35, 5, 5, '2023-03-20'),
(36, 1, 4, '2023-02-10'),
(37, 2, 3, '2023-03-15'),
(38, 3, 1, '2023-02-25'),
(39, 4, 2, '2023-03-10'),
(40, 6, 3, '2023-03-08'),
(41, 7, 2, '2023-02-01'),
(42, 9, 1, '2023-02-15'),
(43, 12, 3, '2023-03-10'),
(44, 15, 2, '2023-02-01'),
(45, 18, 4, '2023-04-05');
create table lesson_views (
id INT primary key,
user_id INT,
lesson_id INT,
viewed_at timestamp,
foreign key (user_id) references users(id),
foreign key (lesson_id) references lessons(id)
);
INSERT INTO lesson_views (id, user_id, lesson_id, viewed_at)
VALUES
(1, 1, 1, '2023-01-16 10:00:00'),
(2, 1, 2, '2023-01-16 10:15:00'),
(3, 1, 3, '2023-01-17 09:00:00'),
(4, 1, 4, '2023-01-17 09:30:00'),
(5, 1, 4, '2023-01-17 11:00:00'),
(6, 1, 4, '2023-01-18 10:00:00'),
(7, 1, 6, '2023-02-02 14:00:00'),
(8, 1, 12, '2023-02-15 10:00:00'),
(9, 1, 13, '2023-02-15 10:30:00'),
(10, 2, 1, '2023-01-21 09:00:00'),
(11, 2, 9, '2023-03-16 10:00:00'),
(12, 2, 10, '2023-03-16 10:30:00'),
(13, 3, 6, '2023-03-06 12:00:00'),
(14, 3, 1, '2023-02-26 11:00:00'),
(15, 3, 2, '2023-02-26 11:20:00'),
(16, 4, 9, '2023-04-02 10:00:00'),
(17, 4, 10, '2023-04-03 10:00:00'),
(18, 4, 6, '2023-03-12 14:00:00'),
(19, 4, 7, '2023-03-13 09:00:00'),
(20, 5, 15, '2023-03-21 10:00:00'),
(21, 5, 16, '2023-03-22 11:00:00'),
(22, 6, 1, '2023-01-07 10:00:00'),
(23, 6, 2, '2023-01-07 10:15:00'),
(24, 6, 3, '2023-01-08 09:00:00'),
(25, 6, 4, '2023-01-08 09:30:00'),
(26, 6, 5, '2023-01-09 10:00:00'),
(27, 6, 6, '2023-01-11 14:00:00'),
(28, 6, 7, '2023-01-12 10:00:00'),
(29, 6, 8, '2023-01-13 11:00:00'),
(30, 6, 12, '2023-02-06 09:00:00'),
(31, 6, 13, '2023-02-06 09:30:00'),
(32, 6, 14, '2023-02-07 10:00:00'),
(33, 6, 9, '2023-03-09 11:00:00'),
(34, 7, 1, '2023-01-14 10:00:00'),
(35, 7, 2, '2023-01-14 10:20:00'),
(36, 7, 3, '2023-01-15 09:00:00'),
(37, 7, 9, '2023-03-11 14:00:00'),
(38, 7, 10, '2023-03-11 14:30:00'),
(39, 9, 12, '2023-02-13 10:00:00'),
(40, 9, 13, '2023-02-13 10:30:00'),
(41, 9, 1, '2023-02-16 11:00:00'),
(42, 12, 1, '2023-01-17 10:00:00'),
(43, 12, 2, '2023-01-17 10:20:00'),
(44, 12, 1, '2023-04-10 11:00:00'),
(45, 12, 3, '2023-04-10 11:30:00'),
(46, 12, 9, '2023-03-12 10:00:00'),
(47, 13, 6, '2023-01-20 14:00:00'),
(48, 13, 7, '2023-01-21 10:00:00'),
(49, 13, 12, '2023-02-21 09:00:00'),
(50, 14, 1, '2023-01-09 08:00:00'),
(51, 14, 2, '2023-01-09 08:15:00'),
(52, 14, 3, '2023-01-10 10:00:00'),
(53, 14, 4, '2023-01-10 10:30:00'),
(54, 14, 5, '2023-01-11 09:00:00'),
(55, 14, 6, '2023-01-10 14:00:00'),
(56, 14, 7, '2023-01-11 10:00:00'),
(57, 14, 8, '2023-01-12 11:00:00'),
(58, 14, 9, '2023-01-10 08:00:00');
---------------------------------------------------------
-- 1 задание Активные пользователи
create materialized view user_activity_summary AS
with enroll_count as (
select u.id as user_id, u.name as user_name,
COUNT(e.course_id) as enrollment_count
from users u
join enrollments e on u.id = e.user_id
group by u.id, u.name
),
viewed_count as (
select ec.user_id, ec.user_name, enrollment_count,
COUNT(distinct lv.lesson_id) as viewed_lessons_count,
MIN(lv.viewed_at) as first_view,
MAX(lv.viewed_at) as last_view
from enroll_count ec
left join lesson_views lv on ec.user_id = lv.user_id
group by ec.user_id, ec.user_name, enrollment_count
),
possible_lessons as (
select u.id as user_id, COUNT(l.id) as poss_lessons
from users u
left join enrollments e on u.id = e.user_id
left join lessons l on e.course_id = l.course_id
group by u.id
)
select vc.user_id, user_name, enrollment_count, viewed_lessons_count, first_view, last_view,
ROUND(100.0 * viewed_lessons_count / pl.poss_lessons, 2) as engagement_rate
from viewed_count vc
left join possible_lessons pl on vc.user_id = pl.user_id
;
------------------------------------------------------------------------------------------------------------------
-- 2 задание Эффективность курсов
create materialized view course_summary AS
with lessons_per_course as (
select c.id as course_id, c.title as course_title,
COUNT(distinct e.user_id) as unique_users,
COUNT(distinct l.id) as lessons_count
from courses c
left join enrollments e on c.id = e.course_id
join lessons l on e.course_id = l.course_id
group by c.id, c.title
),
course_views as (
select l.course_id,
COUNT(lv.id) as total_views,
MIN(lv.viewed_at) as first_viewed_at,
MAX(lv.viewed_at) as last_viewed_at
from lesson_views lv
join lessons l on lv.lesson_id = l.id
group by l.course_id
)
select lpc.course_id, lpc.course_title, lpc.unique_users, lpc.lessons_count,
ROUND(1.00 * cv.total_views / lpc.unique_users, 2) as avg_views_per_user,
cv.first_viewed_at,
cv.last_viewed_at
from lessons_per_course lpc
join course_views cv on lpc.course_id = cv.course_id
order by course_id
;
--------------------------------------------------------------------------------------------------
-- 3 задание Общая отчетность по взаимодействию
create materialized view platform_summary as
with total_counts as (
select COUNT(id) as total_users,
(select COUNT(id) from courses) as total_courses,
(select COUNT(id) from lessons) as total_lessons,
(select COUNT(id) from lesson_views) as total_views,
(select COUNT(distinct user_id) from lesson_views) as users_with_views
from users
)
SELECT total_users, total_courses, total_lessons, users_with_views,
ROUND(1.00 * total_lessons / total_courses, 2) as avg_lessons_per_course,
ROUND(1.00 * total_views / total_lessons, 2) as avg_views_per_lesson
from total_counts
;
-----------------------------------------------------------------------------------------------------
-- 4 Задание Когортный анализ пользователей (Для каждого месяца регистрации)
create materialized view user_cohort_analysis AS
with needed_users as (
select distinct u.id as user_id, u.registration_date as reg_date, e.course_id, lv.id as view_id,
lv.lesson_id as viewed_lesson_id,
FIRST_VALUE(lv.viewed_at) over (partition by lv.user_id order by lv.viewed_at) as first_view_date,
e.user_id as enrolled_users,
lv.user_id as active_users
from users u
left join enrollments e on u.id = e.user_id
left join lesson_views lv on e.user_id = lv.user_id
order by user_id
),
CTE as (
select
TO_CHAR(reg_date, 'YYYY-MM') as cohort_month,
COUNT(distinct user_id) as users_in_cohort,
COUNT(distinct enrolled_users) as enrolled_users,
COUNT(distinct active_users) as active_users,
COUNT(course_id) as courses_count,
extract('days' from AVG(first_view_date - reg_date)) as avg_time_to_first_view
from needed_users
group by cohort_month
)
select cohort_month, users_in_cohort, enrolled_users, active_users,
ROUND(100.0 * active_users / users_in_cohort, 2) as retention_rate,
courses_count / users_in_cohort as avg_courses_per_user,
avg_time_to_first_view
from CTE
;
------------------------------------------------------------------------------------------------------------------
-- 5 Задание Популярность курсов
create materialized view lesson_popularity_ranking as
with lessons_in_courses as (
select l.id as lesson_id, l.title as lesson_title, c.title as course_title,
COUNT(e.user_id) as enrolled_users
from lessons l
join courses c on l.course_id = c.id
join enrollments e on e.course_id = c.id
group by l.id, l.title, c.title
order by lesson_id
),
views_cte as (
select lic.lesson_id,
COUNT(lv.id) as total_views,
COUNT(distinct lv.user_id) as unique_viewers,
1.00 * COUNT(lv.id) / nullif(COUNT(distinct lv.user_id), 0) as avg_views_per_user,
COUNT(lic.enrolled_users) as enrolled_to_course
from lessons_in_courses lic
left join lesson_views lv on lic.lesson_id = lv.lesson_id
group by lic.lesson_id
)
select lic.lesson_id, lic.lesson_title, lic.course_title,
vc.total_views,
vc.unique_viewers,
coalesce(vc.avg_views_per_user, 0) as avg_views_per_user,
dense_rank() OVER (ORDER BY vc.total_views desc) as popularity_rank,
ROUND(100.0 * unique_viewers / enrolled_to_course, 2) as completion_rate
from lessons_in_courses lic
join views_cte vc on lic.lesson_id = vc.lesson_id
;
-----------------------------------------------------------------------------------------------
-- 6 Задания Воронка Конверсии
create materialized view conversion_funnel AS
with course_lessons as (
select c.id as course_id, c.title as course_title,
COUNT(l.id) as total_lessons
from courses c
join lessons l on l.course_id = c.id
group by c.id, c.title
),
registered_users as (
select COUNT(distinct id) as total_users
from users
),
enrolled_users as (
select e.course_id,
COUNT(distinct e.user_id) as enrolled_count
from enrollments e
group by e.course_id
),
started_users as (
select l.course_id,
COUNT(distinct lv.user_id) as started_count
from lessons l
join lesson_views lv on l.id = lv.lesson_id
group by l.course_id
),
user_progress as (
select l.course_id, lv.user_id,
COUNT(distinct lv.lesson_id) as viewed_lessons
from lesson_views lv
join lessons l on lv.lesson_id = l.id
group by l.course_id, lv.user_id
),
mid_progress_users as (
select up.course_id,
COUNT(distinct up.user_id) as mid_progress_count
from user_progress up
join course_lessons cl on up.course_id = cl.course_id
where up.viewed_lessons >= cl.total_lessons * 0.5
group by up.course_id
),
completed_users as (
select up.course_id,
COUNT(distinct up.user_id) as completed_count
from user_progress up
join course_lessons cl on up.course_id = cl.course_id
where up.viewed_lessons >= cl.total_lessons
group by up.course_id
),
funnel_stages as (
select cl.course_id, cl.course_title,
'registered' as stage_name,
ru.total_users as users_count,
1 as stage_order
from course_lessons cl
cross join registered_users ru
union all
select cl.course_id, cl.course_title,
'enrolled' as stage_name,
eu.enrolled_count as users_count,
2 as stage_order
from course_lessons cl
left join enrolled_users eu on cl.course_id = eu.course_id
union all
select cl.course_id, cl.course_title,
'started' as stage_name,
su.started_count as users_count,
3 as stage_order
from course_lessons cl
left join started_users su on cl.course_id = su.course_id
union all
select cl.course_id, cl.course_title,
'mid_progress' as stage_name,
coalesce(mpu.mid_progress_count, 0) as users_count,
4 as stage_order
from course_lessons cl
left join mid_progress_users mpu on cl.course_id = mpu.course_id
union all
select cl.course_id, cl.course_title,
'completed' as stage_name,
coalesce(cu.completed_count, 0) as users_count,
5 as stage_order
from course_lessons cl
left join completed_users cu on cl.course_id = cu.course_id
)
select course_id, course_title, stage_name, users_count,
ROUND(100.0 * users_count / NULLIF(LAG(users_count) over (partition by course_id order by stage_order), 0), 2)
as conversion_from_previous,
ROUND(100.0 * users_count / NULLIF(FIRST_VALUE(users_count) over (partition by course_id order by stage_order), 0), 2)
as conversion_from_start
from funnel_stages
;
-- Признаюсь 6-е задание сделал с помощью нейросети, потому что сам вообще никак к нему подобраться не мог.
-----------------------------------------------------------------------------------------------------------------
-- Доп. Задание №1 Анализ drop-off точек
select course_id, stage_name, users_count,
100 - conversion_from_previous as lost_precentage
from conversion_funnel
where stage_name = 'started'
order by lost_precentage desc
;
--------------------------------------------------------------------------------------------------------------------
-- Доп. Задание №2 Топ-3 урока с повторными просмотрами
select lesson_id
from lesson_popularity_ranking
where avg_views_per_user > 1
limit 3
;
---------------------------------------------------------------------------------------------------------------------
-- Доп. Задание №3 Лучшая когорта по retention
select cohort_month, retention_rate
from user_cohort_analysis
order by retention_rate desc
limit 2
;
-- В нашем случае два месяца с одинаковыми лучшими показателями retention это 2023-01 и 2023-02. Это является следствием того, что у нас
-- наибольшее количество зарегистрированных пользователей именно в эти месяцы, а именно 2023-01 = 40%, а 2023-02 = 20%, соотвественно 60% пользователей были зарегистрированы в эти месяцы.
+525
View File
@@ -0,0 +1,525 @@
-----------------------------------------------------------
--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 и тд.
+1
View File
@@ -0,0 +1 @@
select * from users;
+1
View File
@@ -0,0 +1 @@
select * from users;