CREATE MATERIALIZED VIEW conversion_funnel AS -- CTE какое кол-во человек записалось на курс WITH enrolled_users AS ( SELECT course_id, COUNT(*) AS enrolled FROM pet_enrollments pe GROUP BY course_id ), -- СТЕ на начавших смотреть курс started_users AS ( SELECT pc.id AS course_id, COUNT(DISTINCT plv.user_id) AS started FROM pet_courses pc LEFT JOIN pet_lessons pl ON pc.id = pl.course_id LEFT JOIN pet_lesson_views plv ON plv.lesson_id = pl.id GROUP BY pc.id ), --СТЕ на кол-во просмотров курса и прогресс по курсу user_progress AS ( SELECT pc.id AS course_id, plv.user_id, COUNT(DISTINCT lesson_id) AS count_views, count_lesson_in_course.count_lesson_in_course AS count_lesson_in_course, COUNT(DISTINCT lesson_id) * 1.0 / count_lesson_in_course AS progress FROM pet_courses pc LEFT JOIN pet_lessons pl ON pl.course_id = pc.id LEFT JOIN pet_lesson_views plv ON plv.lesson_id = pl.id 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 = pc.id GROUP BY pc.id, plv.user_id, 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, enrolled_users.enrolled, ROUND(enrolled_users.enrolled * 100.0 / (SELECT COUNT(*) FROM pet_users pu), 2) || '%' AS conversion_from_previous, ROUND(enrolled_users.enrolled * 100.0 / (SELECT COUNT(*) FROM pet_users pu), 2) || '%' AS conversion_from_start FROM pet_courses pc LEFT JOIN enrolled_users ON enrolled_users.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, started_users.started, ROUND(started_users.started * 100.0 / enrolled_users.enrolled, 2) || '%' AS conversion_from_previous, ROUND(started_users.started * 100.0 / (SELECT COUNT(*) FROM pet_users pu), 2) || '%' AS conversion_from_start FROM pet_courses pc LEFT JOIN started_users ON started_users.course_id = pc.id LEFT JOIN enrolled_users ON enrolled_users.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 WHERE user_progress.course_id = pc.id AND user_progress.progress >= 0.5 ), ROUND(( SELECT COUNT(*) FROM user_progress WHERE user_progress.course_id = pc.id AND user_progress.progress >= 0.5) * 100.0 / started_users.started, 2) || '%' AS conversion_from_previous, ROUND(( SELECT COUNT(*) FROM user_progress 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 FROM pet_courses pc LEFT JOIN started_users ON started_users.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 WHERE user_progress.course_id = pc.id AND user_progress.progress >= 1), ROUND(( SELECT COUNT(*) FROM user_progress WHERE user_progress.course_id = pc.id AND user_progress.progress >= 1) * 100.0 / ( SELECT COUNT(*) FROM user_progress WHERE user_progress.course_id = pc.id AND user_progress.progress >= 0.5), 2) || '%' AS conversion_from_previous, ROUND(( SELECT COUNT(*) FROM user_progress WHERE user_progress.course_id = pc.id AND user_progress.progress >= 1) * 100.0 / (SELECT COUNT(*) FROM pet_users pu), 2) || '%' AS conversion_from_start FROM pet_courses pc ORDER BY course_id, stage_order ) -- main select SELECT course_id, course_title, stage_name, users_count, conversion_from_previous, conversion_from_start FROM main_table;