CREATE TABLE users ( id INT PRIMARY KEY, name TEXT, age INT, email TEXT, registration_date DATE ); INSERT INTO users 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'); SELECT * FROM users ORDER BY id; CREATE TABLE courses ( id INT PRIMARY KEY, title TEXT, category TEXT, created_at DATE ); INSERT INTO courses VALUES (1, 'SQL для начинающих', 'data', '2023-01-01'), (2, 'Python для анализа данных', 'programming', '2023-01-15'), (3, 'BI с нуля', 'business', '2023-03-01'); SELECT * FROM courses ORDER BY id; CREATE TABLE lessons ( id INT PRIMARY KEY, course_id INT, title TEXT, duration_min INT, FOREIGN KEY (course_id) REFERENCES courses(id) ); INSERT INTO lessons VALUES (1, 1, 'SELECT и FROM', 10), (2, 1, 'JOIN', 15), (3, 2, 'Pandas', 20), (4, 3, 'Основы BI', 12), (5, 3, 'Метрики и дашборды', 18); SELECT * FROM lessons ORDER BY id; 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 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'); SELECT * FROM enrollments ORDER BY id; 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 VALUES (1, 1, 1, '2023-01-16 10:00:00'), (2, 1, 2, '2023-01-16 10:15:00'), (3, 2, 1, '2023-01-21 09:00:00'), (4, 3, 3, '2023-03-06 12:00:00'), (5, 4, 4, '2023-04-02 10:00:00'), (6, 4, 5, '2023-04-03 10:00:00'); SELECT * FROM lesson_views ORDER BY id; --Создаем materialized view c названием user_activity_summary DROP MATERIALIZED VIEW IF EXISTS user_activity_summary; CREATE MATERIALIZED VIEW user_activity_summary AS WITH enr AS ( SELECT user_id, COUNT(*) AS enrollment_count FROM enrollments GROUP BY user_id ), views AS ( SELECT user_id, COUNT(DISTINCT lesson_id) AS viewed_lessons_count, MIN(viewed_at) AS first_view, MAX(viewed_at) AS last_view FROM lesson_views GROUP BY user_id ), possible AS ( SELECT e.user_id, COUNT(l.id) AS possible_lessons FROM enrollments e JOIN lessons l ON l.course_id = e.course_id GROUP BY e.user_id ) SELECT u.id AS user_id, u.name, enr.enrollment_count, COALESCE(views.viewed_lessons_count, 0) AS viewed_lessons_count, views.last_view, views.first_view, ROUND( COALESCE(views.viewed_lessons_count, 0)::numeric / possible.possible_lessons * 100, 2 ) || '%' AS engagement_rate FROM users u JOIN enr ON enr.user_id = u.id JOIN possible ON possible.user_id = u.id LEFT JOIN views ON views.user_id = u.id ORDER BY u.id; --Проверка SELECT * FROM user_activity_summary ORDER BY user_id; --Создаем materialized view c названием course_summary DROP MATERIALIZED VIEW IF EXISTS course_summary; CREATE MATERIALIZED VIEW course_summary AS WITH -- 1) сколько уроков в каждом курсе lessons_cnt AS ( SELECT course_id, COUNT(*) AS lessons_count FROM lessons GROUP BY course_id ), -- 2) сколько уникальных пользователей записано на курс users_cnt AS ( SELECT course_id, COUNT(DISTINCT user_id) AS unique_users FROM enrollments GROUP BY course_id ), -- 3) просмотры по курсу (через lessons -> course_id) views_cnt 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 lessons l LEFT JOIN lesson_views lv ON lv.lesson_id = l.id GROUP BY l.course_id ) SELECT c.id AS course_id, c.title AS course_title, COALESCE(u.unique_users, 0) AS unique_users, COALESCE(ls.lessons_count, 0) AS lessons_count, COALESCE(v.total_views, 0) AS total_views, ROUND( COALESCE(v.total_views, 0)::numeric / NULLIF(COALESCE(u.unique_users, 0), 0), 2 ) AS avg_views_per_user, v.first_viewed_at, v.last_viewed_at FROM courses c LEFT JOIN users_cnt u ON u.course_id = c.id LEFT JOIN lessons_cnt ls ON ls.course_id = c.id LEFT JOIN views_cnt v ON v.course_id = c.id ORDER BY c.id; SELECT * FROM course_summary ORDER BY course_id; --Создаем materialized view c названием platform_summary DROP MATERIALIZED VIEW IF EXISTS platform_summary; CREATE MATERIALIZED VIEW platform_summary AS SELECT (SELECT COUNT(*) FROM users) AS total_users, (SELECT COUNT(*) FROM courses) AS total_courses, (SELECT COUNT(*) FROM lessons) AS total_lessons, (SELECT COUNT(DISTINCT user_id) FROM lesson_views) AS users_with_views, ROUND( (SELECT COUNT(*) FROM lessons)::numeric / (SELECT COUNT(*) FROM courses), 2 ) AS avg_lessons_per_course, ROUND( (SELECT COUNT(*) FROM lesson_views)::numeric / (SELECT COUNT(*) FROM lessons), 2 ) AS avg_views_per_lesson; SELECT * FROM platform_summary; --Доп. задание: EXPLAIN EXPLAIN SELECT * FROM user_activity_summary; EXPLAIN SELECT * FROM course_summary;