fixed mistakes

This commit is contained in:
Dmitry Medvedev
2026-07-19 20:14:03 +05:00
parent fe1db7ae1e
commit 2dc158dd95
8 changed files with 183 additions and 157 deletions
+13 -8
View File
@@ -1,17 +1,22 @@
--Задание 1. Оформление заказа --Задание 1. Оформление заказа
BEGIN; BEGIN;
INSERT INTO orders (user_id, order_date, status) INSERT INTO orders
VALUES (10, '2025-01-15', 'paid'); (user_id, order_date, status)
VALUES
(10, '2025-01-15', 'paid');
INSERT INTO order_items (product_id, quantity, price_at_purchase) INSERT INTO order_items
VALUES(6, 1, 4800), (7, 1, 5200); (order_id, product_id, quantity, price_at_purchase)
VALUES
(LASTVAL(), 6, 1, 4800),
(LASTVAL(), 7, 1, 5200);
UPDATE products UPDATE products
SET in_stock = TRUE SET in_stock = FALSE
WHERE id IN (6, 7); WHERE id IN (6, 7);
COMMIT; COMMIT;
/*Что в данных пойдёт не так, если часть шагов выполнится, а часть нет? /*Что в данных пойдёт не так, если часть шагов выполнится, а часть нет?
В таком случае транзакция не будет закрыта. Я начал бегин, но произошла ошибка внутри транзакции и поэтому dbeaver дальше ничего выполнять не будет пока я не сделаю откат, либо не скажу завершить. В таком случае транзакция не будет закрыта. Я начал бегин, но произошла ошибка внутри транзакции и поэтому dbeaver дальше ничего выполнять не будет пока я не сделаю откат, либо не скажу завершить.
+1 -1
View File
@@ -20,7 +20,7 @@
--просто прочитает данные. Боб лишь заблокирован для изменений. --просто прочитает данные. Боб лишь заблокирован для изменений.
--что будет с UPDATE users SET country = 'FR' WHERE id = 2;. --что будет с UPDATE users SET country = 'FR' WHERE id = 2;.
--В таком случае БД увидит конфликт и одну из транзакций откатит с ошибкой , а вторую пропустит дальше. --В таком случае БД зависнет (blocked) и будет ждать пока первая транзакция не исполнится.
--Задание 4. Дедлок на двух пользователях --Задание 4. Дедлок на двух пользователях
--Объясни своими словами, почему возникает дедлок и что сделает база с одной из транзакций. --Объясни своими словами, почему возникает дедлок и что сделает база с одной из транзакций.
+2 -2
View File
@@ -50,8 +50,8 @@ WHERE EXTRACT(YEAR FROM created_at) = 2026
SELECT id, name, age SELECT id, name, age
FROM users FROM users
WHERE WHERE
created_at >= '2024-01-01' AND created_at < '2025-01-01' created_at >= '2026-01-01' AND created_at < '2027-01-01'
AND country = 'RU' AND country = 'россия'
AND age > 25; AND age > 25;
Исправлено: выдача колонок "все" заменено на "определенные", логика в where с числами переработана, соответствие определенному году переработано и убрал функцию LOWER. Исправлено: выдача колонок "все" заменено на "определенные", логика в where с числами переработана, соответствие определенному году переработано и убрал функцию LOWER.
+8 -2
View File
@@ -1,12 +1,18 @@
--Задача 1 — Читаем план --Задача 1 — Читаем план
EXPLAIN
SELECT name, email
FROM users
WHERE id = 1;
Какой тип сканирования использует база? Какой тип сканирования использует база?
--SeqScan - сканирование всей таблицы --SeqScan - сканирование всей таблицы
Что означает строка `Index Cond: (id = 1)`? Что означает строка `Index Cond: (id = 1)`?
-- У меня нет такой строки, есть "Filter: (id = 1)". Видимо это фильтрация по определенному значению, но в моем случае это должен быть индекс по столбцу id, но у меня индексы не проставлены, хорошо, что я хотя бы сейчас это уже понимаю. -- Строка означает фильтрацию столбца по индексу, который есть в столбце id.
Почему база использует индекс, а не читает всю таблицу? Почему база использует индекс, а не читает всю таблицу?
--Потому что так быстрее и менее затратно. --В моем случае БД читает всю таблицу, но обычно если индексы расставлены верно и таблица больших размеров, то БД лучше использовать индексы для повышенной производительности.
---------- ----------
--Задача 2 — Seq Scan vs Index Scan --Задача 2 — Seq Scan vs Index Scan
@@ -6,7 +6,8 @@ SELECT
COUNT(DISTINCT pu.id) AS users_in_cohort, COUNT(DISTINCT pu.id) AS users_in_cohort,
COUNT(DISTINCT pe.user_id) AS enrolled_users, COUNT(DISTINCT pe.user_id) AS enrolled_users,
COUNT(DISTINCT plv.user_id) AS active_users, COUNT(DISTINCT plv.user_id) AS active_users,
ROUND(COUNT(DISTINCT plv.user_id) * 100.0 / COUNT(DISTINCT pu.id), 2) || '%' AS retention_rate, --ROUND(COUNT(DISTINCT plv.user_id) * 100.0 / COUNT(DISTINCT pu.id), 2) || '%' AS retention_rate,
ROUND(COUNT(pe.id) * 100.0 / COUNT(DISTINCT pu.id), 2) || '%' AS retention_rate,
ROUND(AVG((SELECT COUNT(pe.id) ROUND(AVG((SELECT COUNT(pe.id)
FROM pet_enrollments pe)) / COUNT(DISTINCT pu.id), 2) AS avg_courses_per_user, FROM pet_enrollments pe)) / COUNT(DISTINCT pu.id), 2) AS avg_courses_per_user,
(SELECT (SELECT
@@ -5,8 +5,8 @@ SELECT
pl.title AS lesson_title, pl.title AS lesson_title,
pc.id AS course_id, pc.id AS course_id,
pc.title AS course_title, pc.title AS course_title,
total_views_lesson.total_views AS total_views, COALESCE(total_views_lesson.total_views, 0) AS total_views,
table_unique_viewers.unique_v AS unique_viewers, COALESCE(table_unique_viewers.unique_v, 0) AS unique_viewers,
ROUND(AVG(views_one_user_per_one_lesson.views_per_viewer), 2) AS avg_views_per_viewer, --также это можно было посчитать total_views/unique_viewers ROUND(AVG(views_one_user_per_one_lesson.views_per_viewer), 2) AS avg_views_per_viewer, --также это можно было посчитать total_views/unique_viewers
DENSE_RANK() OVER ( DENSE_RANK() OVER (
PARTITION BY pc.id PARTITION BY pc.id
@@ -1,47 +1,49 @@
CREATE MATERIALIZED VIEW conversion_funnel AS CREATE MATERIALIZED
VIEW conversion_funnel AS
-- CTE какое кол-во человек записалось на курс -- CTE какое кол-во человек записалось на курс
WITH WITH
enrolled_users AS ( enrolled_users
AS
(
SELECT SELECT
course_id, course_id,
COUNT(*) AS enrolled user_id,
COUNT(*) OVER (PARTITION BY course_id) AS enrolled_count
FROM pet_enrollments pe FROM pet_enrollments pe
GROUP BY course_id ),
),
-- СТЕ на начавших смотреть курс -- СТЕ на начавших смотреть курс
started_users AS ( started_users
AS
(
SELECT SELECT
pc.id AS course_id, eu.course_id,
COUNT(DISTINCT plv.user_id) AS started COUNT(DISTINCT plv.user_id) AS started
FROM pet_courses pc FROM enrolled_users eu -- Базой берем записанных пользователей
LEFT JOIN pet_lessons pl LEFT JOIN pet_lessons pl
ON pc.id = pl.course_id ON eu.course_id = pl.course_id
LEFT JOIN pet_lesson_views plv LEFT JOIN pet_lesson_views plv
ON plv.lesson_id = pl.id ON plv.lesson_id = pl.id AND plv.user_id = eu.user_id
GROUP BY pc.id -- Проверяем, что смотрел именно записанный юзер
), GROUP BY eu.course_id
),
--СТЕ на кол-во просмотров курса и прогресс по курсу --СТЕ на кол-во просмотров курса и прогресс по курсу
user_progress AS ( user_progress
AS
(
SELECT SELECT
pc.id AS course_id, eu.course_id,
plv.user_id, eu.user_id,
COUNT(DISTINCT lesson_id) AS count_views, COUNT(DISTINCT plv.lesson_id) * 1.0 / count_lesson_in_course.count_lesson_in_course AS progress
count_lesson_in_course.count_lesson_in_course AS count_lesson_in_course, FROM enrolled_users eu -- Базой берем записанных пользователей
COUNT(DISTINCT lesson_id) * 1.0 / count_lesson_in_course AS progress
FROM pet_courses pc
LEFT JOIN pet_lessons pl LEFT JOIN pet_lessons pl
ON pl.course_id = pc.id ON pl.course_id = eu.course_id
LEFT JOIN pet_lesson_views plv LEFT JOIN pet_lesson_views plv
ON plv.lesson_id = pl.id ON plv.lesson_id = pl.id AND plv.user_id = eu.user_id -- Просмотры могут быть NULL, если записался, но не смотрел
LEFT JOIN ( LEFT JOIN (
SELECT SELECT
pl.course_id, pl.course_id,
@@ -49,20 +51,20 @@ user_progress AS (
FROM pet_lessons pl FROM pet_lessons pl
GROUP BY pl.course_id GROUP BY pl.course_id
) AS count_lesson_in_course ) AS count_lesson_in_course
ON count_lesson_in_course.course_id = pc.id ON count_lesson_in_course.course_id = eu.course_id
GROUP BY eu.course_id, eu.user_id, count_lesson_in_course.count_lesson_in_course
),
main_table
GROUP BY pc.id, plv.user_id, count_lesson_in_course AS
(
),
main_table AS (
SELECT SELECT
pc.id AS course_id, pc.id AS course_id,
pc.title AS course_title, pc.title AS course_title,
'registered' AS stage_name, 'registered' AS stage_name,
0 AS stage_order, 0 AS stage_order,
(SELECT COUNT(*) FROM pet_users pu) AS users_count, (SELECT COUNT(*)
FROM pet_users pu) AS users_count,
ROUND(100, 2) || '%' AS conversion_from_previous, ROUND(100, 2) || '%' AS conversion_from_previous,
ROUND(100, 2) || '%' AS conversion_from_start ROUND(100, 2) || '%' AS conversion_from_start
@@ -75,13 +77,14 @@ main_table AS (
pc.title AS course_title, pc.title AS course_title,
'enrolled' AS stage_name, 'enrolled' AS stage_name,
1 AS stage_order, 1 AS stage_order,
enrolled_users.enrolled, COALESCE(eu.enrolled_count, 0),
ROUND(enrolled_users.enrolled * 100.0 / (SELECT COUNT(*) FROM pet_users pu), 2) || '%' AS conversion_from_previous, ROUND(COALESCE(eu.enrolled_count, 0) * 100.0 / NULLIF((SELECT COUNT(*) FROM pet_users), 0), 2) || '%' AS conversion_from_previous,
ROUND(enrolled_users.enrolled * 100.0 / (SELECT COUNT(*) FROM pet_users pu), 2) || '%' AS conversion_from_start ROUND(COALESCE(eu.enrolled_count, 0) * 100.0 / NULLIF((SELECT COUNT(*) FROM pet_users), 0), 2) || '%' AS conversion_from_start
FROM pet_courses pc FROM pet_courses pc
LEFT JOIN enrolled_users LEFT JOIN (SELECT course_id, MAX(enrolled_count) AS enrolled_count
ON enrolled_users.course_id = pc.id FROM enrolled_users
GROUP BY course_id) eu
ON eu.course_id = pc.id
UNION ALL UNION ALL
@@ -91,15 +94,16 @@ main_table AS (
pc.title AS course_title, pc.title AS course_title,
'started' AS stage_name, 'started' AS stage_name,
2 AS stage_order, 2 AS stage_order,
started_users.started, COALESCE(su.started, 0),
ROUND(started_users.started * 100.0 / enrolled_users.enrolled, 2) || '%' AS conversion_from_previous, ROUND(COALESCE(su.started, 0) * 100.0 / NULLIF(eu.enrolled_count, 0), 2) || '%' AS conversion_from_previous,
ROUND(started_users.started * 100.0 / (SELECT COUNT(*) FROM pet_users pu), 2) || '%' AS conversion_from_start ROUND(COALESCE(su.started, 0) * 100.0 / NULLIF((SELECT COUNT(*) FROM pet_users), 0), 2) || '%' AS conversion_from_start
FROM pet_courses pc FROM pet_courses pc
LEFT JOIN started_users LEFT JOIN (SELECT course_id, MAX(enrolled_count) AS enrolled_count
ON started_users.course_id = pc.id FROM enrolled_users
LEFT JOIN enrolled_users GROUP BY course_id) eu
ON enrolled_users.course_id = pc.id ON eu.course_id = pc.id
LEFT JOIN started_users su
ON su.course_id = pc.id
UNION ALL UNION ALL
@@ -108,17 +112,18 @@ main_table AS (
pc.title AS course_title, pc.title AS course_title,
'mid_progress' AS stage_name, 'mid_progress' AS stage_name,
3 AS stage_order, 3 AS stage_order,
( SELECT COUNT(*) FROM user_progress (SELECT COUNT(*)
WHERE user_progress.course_id = pc.id AND user_progress.progress >= 0.5 FROM user_progress up
), WHERE up.course_id = pc.id AND up.progress >= 0.5),
ROUND(( SELECT COUNT(*) FROM user_progress ROUND((SELECT COUNT(*)
WHERE user_progress.course_id = pc.id AND user_progress.progress >= 0.5) * 100.0 / started_users.started, 2) || '%' AS conversion_from_previous, FROM user_progress up
ROUND(( SELECT COUNT(*) FROM user_progress WHERE up.course_id = pc.id AND up.progress >= 0.5) * 100.0 / NULLIF(su.started, 0), 2) || '%' AS conversion_from_previous,
WHERE user_progress.course_id = pc.id AND user_progress.progress >= 0.5) * 100.0 / (SELECT COUNT(*) FROM pet_users pu), 2) || '%' AS conversion_from_start ROUND((SELECT COUNT(*)
FROM user_progress up
WHERE up.course_id = pc.id AND up.progress >= 0.5) * 100.0 / NULLIF((SELECT COUNT(*) FROM pet_users), 0), 2) || '%' AS conversion_from_start
FROM pet_courses pc FROM pet_courses pc
LEFT JOIN started_users LEFT JOIN started_users su
ON started_users.course_id = pc.id ON su.course_id = pc.id
UNION ALL UNION ALL
@@ -127,19 +132,17 @@ main_table AS (
pc.title AS course_title, pc.title AS course_title,
'completed' AS stage_name, 'completed' AS stage_name,
4 AS stage_order, 4 AS stage_order,
( SELECT COUNT(*) FROM user_progress (SELECT COUNT(*)
WHERE user_progress.course_id = pc.id AND user_progress.progress >= 1), FROM user_progress up
ROUND(( SELECT COUNT(*) FROM user_progress WHERE up.course_id = pc.id AND up.progress >= 1),
WHERE user_progress.course_id = pc.id AND user_progress.progress >= 1) * 100.0 / ROUND((SELECT COUNT(*)
( SELECT COUNT(*) FROM user_progress FROM user_progress up
WHERE user_progress.course_id = pc.id AND user_progress.progress >= 0.5), 2) || '%' AS conversion_from_previous, WHERE up.course_id = pc.id AND up.progress >= 1) * 100.0 / NULLIF((SELECT COUNT(*) FROM user_progress up WHERE up.course_id = pc.id AND up.progress >= 0.5), 0), 2) || '%' AS conversion_from_previous,
ROUND(( SELECT COUNT(*) FROM user_progress ROUND((SELECT COUNT(*)
WHERE user_progress.course_id = pc.id AND user_progress.progress >= 1) * 100.0 / FROM user_progress up
(SELECT COUNT(*) FROM pet_users pu), 2) || '%' AS conversion_from_start WHERE up.course_id = pc.id AND up.progress >= 1) * 100.0 / NULLIF((SELECT COUNT(*) FROM pet_users), 0), 2) || '%' AS conversion_from_start
FROM pet_courses pc FROM pet_courses pc
)
ORDER BY course_id, stage_order
)
-- main select -- main select
SELECT SELECT
course_id, course_id,
@@ -149,4 +152,5 @@ SELECT
conversion_from_previous, conversion_from_previous,
conversion_from_start conversion_from_start
FROM main_table; FROM main_table
ORDER BY course_id, stage_order;
@@ -1,14 +1,24 @@
-- 1. Задание 1: Анализ drop-off точек -- 1. Задание 1: Анализ drop-off точек
-- Используя conversion_funnel, найди курсы с самым большим процентом отвала между этапами enrolled → started (WHERE stage_name IN (...)) -- Используя conversion_funnel, найди курсы с самым большим процентом отвала между этапами enrolled → started (WHERE stage_name IN (...))
SELECT WITH
drop_off
AS
(
SELECT
course_id, course_id,
stage_name, stage_name,
conversion_from_previous, users_count,
conversion_from_start LAG(users_count) OVER (PARTITION BY course_id) AS prev
FROM conversion_funnel
)
FROM conversion_funnel SELECT
WHERE stage_name IN ('started'); course_id,
ROUND(1 - users_count * 1.0 / NULLIF(prev, 0), 2) * 100 || '%' AS percent_of_drop
FROM drop_off
WHERE stage_name = 'started'
ORDER BY percent_of_drop DESC
-- ОТВЕТ: Самые большие проценты отвала между enrolled и started замечены на курсе 1 и 3 (62 и 67% соответственно). -- ОТВЕТ: Самые большие проценты отвала между enrolled и started замечены на курсе 1 и 3 (62 и 67% соответственно).