исправлены предыдущие ошибки, добавлен полностью блок по кликхаус и пет-проект

This commit is contained in:
Dmitry Medvedev
2026-09-15 15:34:46 +05:00
parent 297df348c1
commit fb0f37912d
11 changed files with 1235 additions and 2 deletions
+283
View File
@@ -0,0 +1,283 @@
Этап 1. Загрузка тестовых данных и создание таблиц
CREATE TABLE dmedvedev.core_dim_course
ON CLUSTER clickhouse_cluster (
course_id UInt32,
title String,
category String
)
ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/dmedvedev/core_dim_course',
'{replica}'
)
ORDER BY course_id;
CREATE TABLE dmedvedev.core_dim_lesson
ON CLUSTER clickhouse_cluster (
lesson_id UInt32,
title String,
duration_min UInt32,
course_id UInt32
)
ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/dmedvedev/core_dim_lesson',
'{replica}'
)
ORDER BY lesson_id;
CREATE TABLE dmedvedev.core_dim_user ON CLUSTER clickhouse_cluster(
user_id UInt32,
name String,
age UInt32,
email String,
registration_date DateTime(3)
)
ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/dmedvedev/core_dim_user',
'{replica}'
)
ORDER BY user_id;
CREATE TABLE dmedvedev.core_enrollments ON CLUSTER clickhouse_cluster(
user_id UInt32,
course_id UInt32,
enrolled_at DateTime(3)
)
ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/dmedvedev/core_enrollments',
'{replica}'
)
ORDER BY user_id;
CREATE TABLE dmedvedev.core_fact_lesson_views ON CLUSTER clickhouse_cluster (
user_id UInt32,
course_id UInt32,
lesson_id UInt32,
viewed_at DateTime(3)
)
ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/dmedvedev/core_fact_lesson_views',
'{replica}'
)
ORDER BY user_id;
--Этап 2. Задание 2.
--СОЗДАЕМ МАТ ПРЕДСТАВЛЕНИЕ mv_lesson_popularity_summary
--для начала создаем таблицу в кликхаусе, реплицируемую с движком ReplicatedAggregatingMergeTree
CREATE TABLE dmedvedev.dm_lesson_popularity_summary ON CLUSTER clickhouse_cluster
(
lesson_id UInt32,
lesson_title String,
course_id UInt32,
course_title String,
total_views SimpleAggregateFunction(sum, UInt64),
unique_users AggregateFunction(uniq, UInt32),
first_view SimpleAggregateFunction(min, DateTime),
last_view SimpleAggregateFunction(max, DateTime)
)
ENGINE = ReplicatedAggregatingMergeTree(
'/clickhouse/tables/{shard}/dmedvedev/dm_lesson_popularity_summary',
'{replica}')
ORDER BY (lesson_id, lesson_title, course_id, course_title);
--далее создаем мат представление, считывает новые данные, делает джойны и кладет данные в dm_lesson_popularity_summary
--
CREATE MATERIALIZED VIEW dmedvedev.mv_lesson_popularity_summary ON CLUSTER clickhouse_cluster
TO dmedvedev.dm_lesson_popularity_summary AS
SELECT
flv.lesson_id AS lesson_id,
dl.title AS lesson_title,
dc.course_id AS course_id,
dc.title AS course_title,
COUNT(*) AS total_views,
uniqState(flv.user_id) AS unique_users,
MIN(flv.viewed_at) AS first_view,
MAX(flv.viewed_at) AS last_view
FROM dmedvedev.core_fact_lesson_views AS flv
LEFT JOIN dmedvedev.core_dim_lesson AS dl ON flv.lesson_id = dl.lesson_id
LEFT JOIN dmedvedev.core_dim_course AS dc ON dl.course_id = dc.course_id
GROUP BY lesson_id, lesson_title, course_id, course_title;
-- помещаем данные в dm_lesson_popularity_summary
INSERT INTO dmedvedev.dm_lesson_popularity_summary
SELECT
flv.lesson_id AS lesson_id,
dl.title AS lesson_title,
dc.course_id AS course_id,
dc.title AS course_title,
COUNT(*) AS total_views,
uniqState(flv.user_id) AS unique_users,
MIN(flv.viewed_at) AS first_view,
MAX(flv.viewed_at) AS last_view
FROM dmedvedev.core_fact_lesson_views AS flv
LEFT JOIN dmedvedev.core_dim_lesson AS dl ON flv.lesson_id = dl.lesson_id
LEFT JOIN dmedvedev.core_dim_course AS dc ON dl.course_id = dc.course_id
GROUP BY lesson_id, lesson_title, course_id, course_title;
ЗАПРОС (ТОП 5 самых просматриваемых уроков уникальными пользователями):
SELECT
lesson_id,
lesson_title,
total_views,
uniqMerge(unique_users) AS unique_users
FROM dmedvedev.dm_lesson_popularity_summary dlps
GROUP BY lesson_id, lesson_title, course_id, course_title, total_views, first_view, last_view
ORDER BY unique_users DESC
LIMIT 5;
--Задание 3. СОЗДАЕМ МАТ ПРЕДСТАВЛЕНИЕ mv_inactive_users_summary
--для начала создаем таблицу в кликхаусе, реплицируемую с движком ReplicatedAggregatingMergeTree
CREATE TABLE dmedvedev.dm_inactive_users_summary ON CLUSTER clickhouse_cluster
(
user_id UInt32,
name String,
email String,
age UInt32,
registrated_courses_count AggregateFunction(uniqExact, UInt32),
registration_date DateTime
)
ENGINE = ReplicatedAggregatingMergeTree(
'/clickhouse/tables/{shard}/dmedvedev/dm_inactive_users_summary',
'{replica}'
)
ORDER BY (user_id, name, email, age, registration_date);
--создаем само мат представление, которое перехватывает данные из таблицы фактов и помещает в кликхаус dmedvedev.dm_inactive_users_summary
CREATE MATERIALIZED VIEW dmedvedev.mv_inactive_users_summary ON CLUSTER clickhouse_cluster
TO dmedvedev.dm_inactive_users_summary
AS
SELECT
ce.user_id,
cdu.name,
cdu.email,
cdu.age,
uniqExactState(ce.course_id) AS registrated_courses_count,
cdu.registration_date
FROM core_enrollments ce
LEFT JOIN core_dim_user cdu ON ce.user_id = cdu.user_id
GROUP BY ce.user_id, cdu.name, cdu.email, cdu.age, cdu.registration_date;
-- помещаем данные в dm_inactive_users_summary
INSERT INTO dmedvedev.dm_inactive_users_summary
SELECT
ce.user_id,
cdu.name,
cdu.email,
cdu.age,
uniqExactState(ce.course_id) AS registrated_courses_count,
cdu.registration_date
FROM core_enrollments ce
LEFT JOIN core_dim_user cdu ON ce.user_id = cdu.user_id
GROUP BY ce.user_id, cdu.name, cdu.email, cdu.age, cdu.registration_date;
--читаем данные из dm_inactive_users_summary
SELECT
user_id,
name,
email,
age,
uniqExactMerge(registrated_courses_count) AS registrated_courses_count,
registration_date
FROM dmedvedev.dm_inactive_users_summary
GROUP BY user_id, name, email, age, registration_date;
ЗАПРОС (ученики зарегистрировавшиеся только на 1 курс):
SELECT
user_id,
name,
email,
age,
uniqExactMerge(registrated_courses_count) AS registrated_courses_count,
registration_date
FROM dmedvedev.dm_inactive_users_summary
GROUP BY user_id, name, email, age, registration_date
HAVING registrated_courses_count = 1;
--Задание 4. создаем витрину course_completion_rate
--для начала создаем таблицу в кликхаус с движком
CREATE TABLE dmedvedev.dm_course_completion_rate ON CLUSTER clickhouse_cluster
(
user_id UInt32,
name String,
course_id UInt32,
course_title String,
lessons_in_course SimpleAggregateFunction(max, UInt32),
lessons_viewed AggregateFunction(uniqExact, UInt32)
)
ENGINE = ReplicatedAggregatingMergeTree(
'/clickhouse/tables/{shard}/dmedvedev/dm_course_completion_rate',
'{replica}'
)
ORDER BY (user_id, course_id, name, course_title);
--далее создаем мат представление
CREATE MATERIALIZED VIEW dmedvedev.mv_course_completion_rate ON CLUSTER clickhouse_cluster
TO dmedvedev.dm_course_completion_rate
AS
SELECT
cflv.user_id AS user_id,
cdu.name AS name,
cdl.course_id AS course_id,
cdc.title AS course_title,
max(count_lesson_in_course.total_leeson) AS lessons_in_course,
uniqExactState(cflv.lesson_id) AS lessons_viewed
FROM core_fact_lesson_views cflv
LEFT JOIN core_dim_user cdu ON cflv.user_id = cdu.user_id
LEFT JOIN core_dim_lesson cdl ON cflv.lesson_id = cdl.lesson_id
LEFT JOIN core_dim_course cdc ON cdl.course_id = cdc.course_id
LEFT JOIN (
SELECT course_id, uniqExact(lesson_id) AS total_leeson
FROM dmedvedev.core_dim_lesson cdm
GROUP BY course_id
) AS count_lesson_in_course ON count_lesson_in_course.course_id = cdl.course_id
GROUP BY cflv.user_id, cdl.course_id, cdu.name, cdc.title;
--вставляем данные
INSERT INTO dmedvedev.dm_course_completion_rate
SELECT
cflv.user_id AS user_id,
cdu.name AS name,
cdl.course_id AS course_id,
cdc.title AS course_title,
max(count_lesson_in_course.total_leeson) AS lessons_in_course,
uniqExactState(cflv.lesson_id) AS lessons_viewed
FROM core_fact_lesson_views cflv
LEFT JOIN core_dim_user cdu ON cflv.user_id = cdu.user_id
LEFT JOIN core_dim_lesson cdl ON cflv.lesson_id = cdl.lesson_id
LEFT JOIN core_dim_course cdc ON cdl.course_id = cdc.course_id
LEFT JOIN (
SELECT course_id, uniqExact(lesson_id) AS total_leeson
FROM dmedvedev.core_dim_lesson cdm
GROUP BY course_id
) AS count_lesson_in_course ON count_lesson_in_course.course_id = cdl.course_id
GROUP BY cflv.user_id, cdl.course_id, cdu.name, cdc.title;
ЗАПРОС: ученики просмотревшие какой-либо курс менее чем на половину
SELECT
user_id,
name,
course_id,
course_title,
max(lessons_in_course) AS total_lessons,
-- Объединяем просмотры:
uniqExactMerge(lessons_viewed) AS viewed_lessons,
IF(total_lessons > 0, CAST(viewed_lessons AS Float64) / total_lessons, 0.0) AS completion_rate
FROM dmedvedev.dm_course_completion_rate
GROUP BY user_id, name, course_id, course_title
HAVING completion_rate <= 0.5
ORDER BY user_id, course_id;
--ЭТАП 4. Тестирование и валидация
В ДАННОМ ЗАДАНИИ Я НЕ ПРОИЗВОДИЛ СРАВНЕНИЕ ТАБЛИЦ ИЗ ГП И КХ, ТАК КАК ТАКБЛИЦЫ
С ОДИНАКОВЫМИ НАЗВАНИЯМИ ПРОСТО НАПРОСТО СЧИТАЮТ РАЗНЫЕ ПОКАЗАТЕЛИ.
ПОЧЕМУ ТАК ПРОИЗОШЛО? ПОТОМУ ЧТО В ЗАДАНИИ КХ ДАНЫ СОВСЕМ ДРУГИЕ ПОКАЗАЛИТЕ, ПОЭТОМУ ТАБЛИЦЫ РАЗЛИЧАЮТСЯ ПО НАПОЛНЕНИЮ.
ЧТО Я ЗАМЕТИЛ ПРИ СОЗДАНИИ МАТ ПРЕДСТАВЛЕНИЙ В КХ?
ТО ЧТО В ГП МАТ ПРЕДСТАВЛЕНИЯ ЭТО ОТДЕЛЬНАЯ ТАБЛИЦА, КОТОРАЯ ХРАНИТ ОПРЕДЕЛЕННЫЙ РЕЗУЛЬТАТ, ОНА НЕ ЯВЛЕТСЯ ДИНАМИЧЕСКОЙ,
ЕЕ НЕОБХОДИМО ОБНОВЛЯТЬ ПРИ ПОСТУПЛЕНИИ НОВЫХ ДАННЫХ, СООТВЕТСТВЕННО ВЕСЬ АНАЛИЗ ДАННЫХ ГП ДЕЛАЕТ ПО ВСЕМ ДАННЫМ ЗАПРОСА.
КХ В ОТЛИЧИЕ ОТ ГП РАБОТАЕТ ИНАЧЕ, ОН ПОДХВАТЫВАЕТ ЛИШЬ НОВЫЕ ДАННЫЕ, ПРИЧЕМ ЭТИ НОВЫЕ ДАННЫЕ МОГУТ ПОСТУПАТЬ ТОЛЬКО ИЗ ТАБЛИЦЫ ФАКТОВ,
ЕСЛИ ОНИ ПОСТУПЯТ ИЗ ТАБЛИЦ ИЗМЕРЕНИЙ, ТО НИКАКИХ РАСЧЕТОМ КХ ДЕЛАТЬ НЕ БУДЕТ.
ТАКЖЕ СТОИТ ОТМЕТИТЬ, ЧТО В КХ ПОЯВЛЯЕТСЯ ЦЕЛЕВАЯ ТАБЛИЦА, В КОТОРУЮ МАТ ПРЕДСТАВЛЕНИЕ И ПРОИЗВОДИТ ЗАПИСЬ ДАННЫХ И ПОТОМ
В ЗАПРОСЕ МЫ ОБРАЩАЕМСЯ ИМЕННО К ЦЕЛЕВОЙ ТАБЛИЦЕ, А НЕ К МАТ ПРЕДСТАВЛЕНИЮ (МАТ ПРЕДСТАВЛЕНИЕ ЭТО ВСЕГО ЛИШЬ ТРИГГЕР, КОТОРЫЕ УЛАВЛИВАЕТ НОВЫЕ ДАННЫЕ ИЗ ТАБЛИЦЫ ФАКТОВ).