CREATE MATERIALIZED VIEW conversion_funnel AS -- CTE какое кол-во человек записалось на курс WITH enrolled_users AS ( SELECT course_id, user_id, COUNT(*) OVER (PARTITION BY course_id) AS enrolled_count FROM pet_enrollments pe ), -- СТЕ на начавших смотреть курс started_users AS ( SELECT eu.course_id, COUNT(DISTINCT plv.user_id) AS started FROM enrolled_users eu -- Базой берем записанных пользователей LEFT JOIN pet_lessons pl ON eu.course_id = pl.course_id LEFT JOIN pet_lesson_views plv ON plv.lesson_id = pl.id AND plv.user_id = eu.user_id -- Проверяем, что смотрел именно записанный юзер GROUP BY eu.course_id ), --СТЕ на кол-во просмотров курса и прогресс по курсу user_progress AS ( SELECT eu.course_id, eu.user_id, COUNT(DISTINCT plv.lesson_id) * 1.0 / count_lesson_in_course.count_lesson_in_course AS progress FROM enrolled_users eu -- Базой берем записанных пользователей LEFT JOIN pet_lessons pl ON pl.course_id = eu.course_id LEFT JOIN pet_lesson_views plv ON plv.lesson_id = pl.id AND plv.user_id = eu.user_id -- Просмотры могут быть NULL, если записался, но не смотрел LEFT JOIN ( SELECT pl.course_id, COUNT(*) AS count_lesson_in_course FROM pet_lessons pl GROUP BY pl.course_id ) AS count_lesson_in_course 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 AS ( SELECT pc.id AS course_id, pc.title AS course_title, 'registered' AS stage_name, 0 AS stage_order, (SELECT COUNT(*) FROM pet_users pu) AS users_count, ROUND(100, 2) || '%' AS conversion_from_previous, ROUND(100, 2) || '%' AS conversion_from_start FROM pet_courses pc UNION ALL SELECT pc.id AS course_id, pc.title AS course_title, 'enrolled' AS stage_name, 1 AS stage_order, COALESCE(eu.enrolled_count, 0), ROUND(COALESCE(eu.enrolled_count, 0) * 100.0 / NULLIF((SELECT COUNT(*) FROM pet_users), 0), 2) || '%' AS conversion_from_previous, ROUND(COALESCE(eu.enrolled_count, 0) * 100.0 / NULLIF((SELECT COUNT(*) FROM pet_users), 0), 2) || '%' AS conversion_from_start FROM pet_courses pc LEFT JOIN (SELECT course_id, MAX(enrolled_count) AS enrolled_count FROM enrolled_users GROUP BY course_id) eu ON eu.course_id = pc.id UNION ALL SELECT pc.id AS course_id, pc.title AS course_title, 'started' AS stage_name, 2 AS stage_order, COALESCE(su.started, 0), ROUND(COALESCE(su.started, 0) * 100.0 / NULLIF(eu.enrolled_count, 0), 2) || '%' AS conversion_from_previous, ROUND(COALESCE(su.started, 0) * 100.0 / NULLIF((SELECT COUNT(*) FROM pet_users), 0), 2) || '%' AS conversion_from_start FROM pet_courses pc LEFT JOIN (SELECT course_id, MAX(enrolled_count) AS enrolled_count FROM enrolled_users GROUP BY course_id) eu ON eu.course_id = pc.id LEFT JOIN started_users su ON su.course_id = pc.id UNION ALL SELECT pc.id AS course_id, pc.title AS course_title, 'mid_progress' AS stage_name, 3 AS stage_order, (SELECT COUNT(*) FROM user_progress up WHERE up.course_id = pc.id AND up.progress >= 0.5), ROUND((SELECT COUNT(*) FROM user_progress up WHERE up.course_id = pc.id AND up.progress >= 0.5) * 100.0 / NULLIF(su.started, 0), 2) || '%' AS conversion_from_previous, 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 LEFT JOIN started_users su ON su.course_id = pc.id UNION ALL SELECT pc.id AS course_id, pc.title AS course_title, 'completed' AS stage_name, 4 AS stage_order, (SELECT COUNT(*) FROM user_progress up WHERE up.course_id = pc.id AND up.progress >= 1), ROUND((SELECT COUNT(*) FROM user_progress up 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 up 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 ) -- main select SELECT course_id, course_title, stage_name, users_count, conversion_from_previous, conversion_from_start FROM main_table ORDER BY course_id, stage_order;