diff --git a/amaksatov/postgresql/OS-329.sql b/amaksatov/postgresql/OS-329.sql new file mode 100644 index 0000000..3f0f080 --- /dev/null +++ b/amaksatov/postgresql/OS-329.sql @@ -0,0 +1,215 @@ +CREATE TABLE IF NOT EXISTS amaksatov.users ( + id SERIAL PRIMARY KEY, + name VARCHAR(100), + age INT, + email VARCHAR(255), + registration_date DATE +); + +CREATE TABLE IF NOT EXISTS amaksatov.courses ( + id SERIAL PRIMARY KEY, + title VARCHAR(255), + category VARCHAR(100), + created_at DATE +); + +CREATE TABLE IF NOT EXISTS amaksatov.lessons ( + id SERIAL PRIMARY KEY, + course_id INT, + title VARCHAR(255), + duration_min INT, + FOREIGN KEY (course_id) REFERENCES amaksatov.courses(id) +); + +CREATE TABLE IF NOT EXISTS amaksatov.enrollments ( + id SERIAL PRIMARY KEY, + user_id INT, + course_id INT, + enrolled_at date, + FOREIGN KEY (user_id) REFERENCES amaksatov.users(id), + FOREIGN KEY (course_id) REFERENCES amaksatov.courses(id) +); + +CREATE TABLE IF NOT EXISTS amaksatov.lesson_views ( + id SERIAL PRIMARY KEY, + user_id INT, + lesson_id INT, + viewed_at TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES amaksatov.users(id), + FOREIGN KEY (lesson_id) REFERENCES amaksatov.lessons(id) +); + + +INSERT + INTO + amaksatov.users ( + name, + age, + email, + registration_date + ) +VALUES ( + 'Alice', + 25, + 'alice@mail.com', + '2023-01-10' +), +( + 'Bob', + 30, + 'bob@gmail.com', + '2023-02-05' +), +( + 'Charlie', + 22, + 'charlie@mail.com', + '2023-02-20' +), +( + 'Diana', + 28, + 'diana@mail.com', + '2023-03-01' +), +( + 'Ethan', + 35, + 'ethan@gmail.com', + '2023-03-10' +); + +SELECT * FROM amaksatov.users; + + +INSERT + INTO + amaksatov.courses ( + title, + category, + created_at + ) +VALUES +( + 'SQL для начинающих', + 'data', + '2023-01-01' +), +( + 'Python для анализа данных', + 'programming', + '2023-01-15' +), +( + 'BI с нуля', + 'business', + '2023-03-01' +); + +INSERT + INTO + amaksatov.lessons ( + course_id, + title, + duration_min + ) +VALUES +( + 1, + 'SELECT и FROM', + 10 +), +( + 1, + 'JOIN', + 15 +), +( + 2, + 'Pandas', + 20 +), +( + 3, + 'Основы BI', + 12 +), +( + 3, + 'Метрики и дашборды', + 18 +); + +INSERT + INTO + amaksatov.enrollments ( + user_id, + course_id, + enrolled_at + ) +VALUES +( + 1, + 1, + '2023-01-15' +), +( + 1, + 2, + '2023-02-01' +), +( + 2, + 1, + '2023-01-20' +), +( + 3, + 2, + '2023-03-05' +), +( + 4, + 3, + '2023-04-01' +); + +INSERT + INTO + amaksatov.lesson_views ( + user_id, + lesson_id, + viewed_at + ) +VALUES +( + 1, + 1, + '2023-01-16 10:00:00' +), +( + 1, + 2, + '2023-01-16 10:15:00' +), +( + 2, + 1, + '2023-01-21 09:00:00' +), +( + 3, + 3, + '2023-03-06 12:00:00' +), +( + 4, + 4, + '2023-04-02 10:00:00' +), +( + 4, + 5, + '2023-04-03 10:00:00' +); + diff --git a/amaksatov/postgresql/OS-330.sql b/amaksatov/postgresql/OS-330.sql new file mode 100644 index 0000000..9b78b52 --- /dev/null +++ b/amaksatov/postgresql/OS-330.sql @@ -0,0 +1,68 @@ + +CREATE MATERIALIZED VIEW user_activity_summary AS +SELECT + u.id AS user_id, -- user_id + u.name, -- name + COUNT(DISTINCT e.course_id) AS enrollment_count, -- кол-во записей на курсы + COUNT(DISTINCT lv.lesson_id) AS viewed_lessons_count, -- кол-во просмотренных уроков + MAX(lv.viewed_at) AS last_viewed, -- последний просмотр + MIN(lv.viewed_at) AS first_viewed, -- первый просмотр + ROUND(COUNT(DISTINCT l.course_id) * 100.0 / COUNT(DISTINCT l.id), 2) || '%' AS engagement_rate -- вовлеченность(процент просмотренных/записанным) +FROM amaksatov.users u -- основная таблица +JOIN enrollments e ON e.user_id = u.id -- связь users с enrollments +LEFT JOIN lesson_views lv ON lv.user_id = e.user_id -- связь lessons с lesson_views +JOIN lessons l ON l.course_id = e.course_id -- связь уроков с курсами пользователя +GROUP BY u.id, u.name -- группировка по user_id и name +ORDER BY user_id; -- сортировка по user_id + + +CREATE MATERIALIZED VIEW course_summary AS +SELECT + c.id AS course_id, + c.title AS course_title, + COUNT(DISTINCT e.user_id) AS unique_users, + COUNT(DISTINCT l.id) AS lessons_count, + COUNT(DISTINCT lv.id) AS total_views, + ROUND( + CASE + WHEN COUNT(DISTINCT e.user_id) = 0 THEN NULL + ELSE COUNT(DISTINCT lv.id) * 1.0 / COUNT(DISTINCT e.user_id) + END, 2) AS avg_views_per_user, + MIN(lv.viewed_at) AS first_viewed_at, + MAX(lv.viewed_at) AS last_viewed_at +FROM courses c +LEFT JOIN enrollments e ON e.course_id = c.id +LEFT JOIN lessons l ON c.id = l.course_id +LEFT JOIN lesson_views lv ON lv.lesson_id = l.id +GROUP BY c.id; + + + + + +CREATE MATERIALIZED VIEW platform_summary AS +WITH +user_stats AS ( + SELECT COUNT(*) AS total_users FROM users +), +course_stats AS ( + SELECT COUNT(*) AS total_courses FROM courses +), +lesson_stats AS ( + SELECT COUNT(*) AS total_lessons FROM lessons +), +view_stats AS ( + SELECT COUNT(*) AS total_views, COUNT(DISTINCT user_id) AS users_with_views + FROM lesson_views +) + +SELECT + u.total_users, + c.total_courses, + l.total_lessons, + v.users_with_views, + + ROUND(l.total_lessons::decimal / NULLIF(c.total_courses, 0), 2) AS avg_lessons_per_course, + ROUND(v.total_views::decimal / NULLIF(l.total_lessons, 0), 2) AS avg_views_per_lesson + +FROM user_stats u, course_stats c, lesson_stats l, view_stats v;