From 781cb67a2cf0064596aa330b23489da5d606d418 Mon Sep 17 00:00:00 2001 From: Azamat Date: Thu, 8 Jan 2026 19:43:07 +0600 Subject: [PATCH 1/9] add postgresql script.sql --- amaksatov/postgresql/script.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 amaksatov/postgresql/script.sql diff --git a/amaksatov/postgresql/script.sql b/amaksatov/postgresql/script.sql new file mode 100644 index 0000000..a2b9b92 --- /dev/null +++ b/amaksatov/postgresql/script.sql @@ -0,0 +1 @@ +SELECT * FROM table1; \ No newline at end of file From 717ba15e01db914b5dbf363ede0082c3df2db66b Mon Sep 17 00:00:00 2001 From: Azamat Date: Thu, 8 Jan 2026 19:45:59 +0600 Subject: [PATCH 2/9] test changes --- amaksatov/postgresql/script.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amaksatov/postgresql/script.sql b/amaksatov/postgresql/script.sql index a2b9b92..e5fbf49 100644 --- a/amaksatov/postgresql/script.sql +++ b/amaksatov/postgresql/script.sql @@ -1 +1 @@ -SELECT * FROM table1; \ No newline at end of file +SELECT * FROM table3; \ No newline at end of file From 47732aa19a9f882e47d0abd954ae4c7465f0e4c0 Mon Sep 17 00:00:00 2001 From: Azamat Date: Sat, 10 Jan 2026 20:11:38 +0600 Subject: [PATCH 3/9] Done --- amaksatov/postgresql/OS-329.sql | 217 ++++++++++++++++++++++++++++++++ amaksatov/postgresql/script.sql | 1 - 2 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 amaksatov/postgresql/OS-329.sql diff --git a/amaksatov/postgresql/OS-329.sql b/amaksatov/postgresql/OS-329.sql new file mode 100644 index 0000000..a3d3ea3 --- /dev/null +++ b/amaksatov/postgresql/OS-329.sql @@ -0,0 +1,217 @@ +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' +); + + +SELECT * FROM amaksatov.lessons \ No newline at end of file diff --git a/amaksatov/postgresql/script.sql b/amaksatov/postgresql/script.sql index e5fbf49..e69de29 100644 --- a/amaksatov/postgresql/script.sql +++ b/amaksatov/postgresql/script.sql @@ -1 +0,0 @@ -SELECT * FROM table3; \ No newline at end of file From 036eb440a8f2d7532c81b763c93e9a0de1f1c1b6 Mon Sep 17 00:00:00 2001 From: Azamat Date: Sat, 10 Jan 2026 20:13:58 +0600 Subject: [PATCH 4/9] fixed --- amaksatov/postgresql/OS-329.sql | 2 -- 1 file changed, 2 deletions(-) diff --git a/amaksatov/postgresql/OS-329.sql b/amaksatov/postgresql/OS-329.sql index a3d3ea3..3f0f080 100644 --- a/amaksatov/postgresql/OS-329.sql +++ b/amaksatov/postgresql/OS-329.sql @@ -213,5 +213,3 @@ VALUES '2023-04-03 10:00:00' ); - -SELECT * FROM amaksatov.lessons \ No newline at end of file From 47599c3420bc1e90222e99a2903b021fa607980b Mon Sep 17 00:00:00 2001 From: Azamat Date: Sat, 10 Jan 2026 21:24:36 +0600 Subject: [PATCH 5/9] Done --- amaksatov/postgresql/OS-330.sql | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 amaksatov/postgresql/OS-330.sql diff --git a/amaksatov/postgresql/OS-330.sql b/amaksatov/postgresql/OS-330.sql new file mode 100644 index 0000000..86a034c --- /dev/null +++ b/amaksatov/postgresql/OS-330.sql @@ -0,0 +1,19 @@ +CREATE MATERIALIZED VIEW user_activity_summary AS +SELECT + u.id AS user_id, + u.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 ( + CASE + WHEN COUNT(DISTINCT l.id) = 0 THEN NULL + ELSE COUNT(DISTINCT lv.lesson_id) * 1.0 / COUNT(DISTINCT l.id) * 100 + END, 2)::TEXT || '%' AS "engagement_rate" +FROM amaksatov.users u +LEFT JOIN enrollments e ON e.user_id = u.id +LEFT JOIN lessons l ON l.course_id = e.course_id +LEFT JOIN lesson_views lv ON lv.lesson_id = l.id AND u.id = lv.user_id +GROUP BY u.id +HAVING COUNT(e.id) > 0; \ No newline at end of file From 43e731927f60fef4ba70b2974dde0c3aff4d79a4 Mon Sep 17 00:00:00 2001 From: Azamat Date: Sat, 10 Jan 2026 21:26:08 +0600 Subject: [PATCH 6/9] OS-330 --- amaksatov/postgresql/script.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 amaksatov/postgresql/script.sql diff --git a/amaksatov/postgresql/script.sql b/amaksatov/postgresql/script.sql deleted file mode 100644 index e69de29..0000000 From 5edbdc2e8c3b16e144c19aa03d6eeb17a25965b7 Mon Sep 17 00:00:00 2001 From: Azamat Date: Sun, 11 Jan 2026 16:53:12 +0600 Subject: [PATCH 7/9] Fixed --- amaksatov/postgresql/OS-330.sql | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/amaksatov/postgresql/OS-330.sql b/amaksatov/postgresql/OS-330.sql index 86a034c..beeae0e 100644 --- a/amaksatov/postgresql/OS-330.sql +++ b/amaksatov/postgresql/OS-330.sql @@ -6,14 +6,10 @@ SELECT COUNT(DISTINCT lv.lesson_id) AS viewed_lessons_count, MAX(lv.viewed_at) AS last_viewed, MIN(lv.viewed_at) AS first_viewed, - ROUND ( - CASE - WHEN COUNT(DISTINCT l.id) = 0 THEN NULL - ELSE COUNT(DISTINCT lv.lesson_id) * 1.0 / COUNT(DISTINCT l.id) * 100 - END, 2)::TEXT || '%' AS "engagement_rate" + ROUND (COUNT(DISTINCT lv.lesson_id) * 1.0 / NULLIF(COUNT(DISTINCT l.id), 0) * 100, 2) AS "engagement_rate(%)" FROM amaksatov.users u LEFT JOIN enrollments e ON e.user_id = u.id LEFT JOIN lessons l ON l.course_id = e.course_id LEFT JOIN lesson_views lv ON lv.lesson_id = l.id AND u.id = lv.user_id -GROUP BY u.id -HAVING COUNT(e.id) > 0; \ No newline at end of file +GROUP BY u.id; + From 049de3c93a72fbbaab7af5854516cb7dd45cb89f Mon Sep 17 00:00:00 2001 From: Azamat Date: Sun, 11 Jan 2026 17:47:05 +0600 Subject: [PATCH 8/9] Final fix --- amaksatov/postgresql/OS-330.sql | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/amaksatov/postgresql/OS-330.sql b/amaksatov/postgresql/OS-330.sql index beeae0e..4745707 100644 --- a/amaksatov/postgresql/OS-330.sql +++ b/amaksatov/postgresql/OS-330.sql @@ -1,15 +1,16 @@ -CREATE MATERIALIZED VIEW user_activity_summary AS -SELECT - u.id AS user_id, - u.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 lv.lesson_id) * 1.0 / NULLIF(COUNT(DISTINCT l.id), 0) * 100, 2) AS "engagement_rate(%)" -FROM amaksatov.users u -LEFT JOIN enrollments e ON e.user_id = u.id -LEFT JOIN lessons l ON l.course_id = e.course_id -LEFT JOIN lesson_views lv ON lv.lesson_id = l.id AND u.id = lv.user_id -GROUP BY u.id; +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 From ebe1f007160a424ca17c4f10c7bf2d45019a22d0 Mon Sep 17 00:00:00 2001 From: Azamat Date: Sun, 11 Jan 2026 18:47:48 +0600 Subject: [PATCH 9/9] Full pet-project --- amaksatov/postgresql/OS-330.sql | 52 +++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/amaksatov/postgresql/OS-330.sql b/amaksatov/postgresql/OS-330.sql index 4745707..9b78b52 100644 --- a/amaksatov/postgresql/OS-330.sql +++ b/amaksatov/postgresql/OS-330.sql @@ -14,3 +14,55 @@ LEFT JOIN lesson_views lv ON lv.user_id = e.user_id -- связь lessons с les 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;