fixed mistakes
This commit is contained in:
@@ -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 дальше ничего выполнять не будет пока я не сделаю откат, либо не скажу завершить.
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
--просто прочитает данные. Боб лишь заблокирован для изменений.
|
--просто прочитает данные. Боб лишь заблокирован для изменений.
|
||||||
|
|
||||||
--что будет с UPDATE users SET country = 'FR' WHERE id = 2;.
|
--что будет с UPDATE users SET country = 'FR' WHERE id = 2;.
|
||||||
--В таком случае БД увидит конфликт и одну из транзакций откатит с ошибкой , а вторую пропустит дальше.
|
--В таком случае БД зависнет (blocked) и будет ждать пока первая транзакция не исполнится.
|
||||||
|
|
||||||
--Задание 4. Дедлок на двух пользователях
|
--Задание 4. Дедлок на двух пользователях
|
||||||
--Объясни своими словами, почему возникает дедлок и что сделает база с одной из транзакций.
|
--Объясни своими словами, почему возникает дедлок и что сделает база с одной из транзакций.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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% соответственно).
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user