diff --git a/ebelykh/postgresql/ПЭТ проект/Script.sql b/ebelykh/postgresql/ПЭТ проект/Script.sql new file mode 100644 index 0000000..25727d1 --- /dev/null +++ b/ebelykh/postgresql/ПЭТ проект/Script.sql @@ -0,0 +1,233 @@ +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; + + + \ No newline at end of file