исправлены предыдущие ошибки, добавлен полностью блок по кликхаус и пет-проект
This commit is contained in:
@@ -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. Тестирование и валидация
|
||||
В ДАННОМ ЗАДАНИИ Я НЕ ПРОИЗВОДИЛ СРАВНЕНИЕ ТАБЛИЦ ИЗ ГП И КХ, ТАК КАК ТАКБЛИЦЫ
|
||||
С ОДИНАКОВЫМИ НАЗВАНИЯМИ ПРОСТО НАПРОСТО СЧИТАЮТ РАЗНЫЕ ПОКАЗАТЕЛИ.
|
||||
ПОЧЕМУ ТАК ПРОИЗОШЛО? ПОТОМУ ЧТО В ЗАДАНИИ КХ ДАНЫ СОВСЕМ ДРУГИЕ ПОКАЗАЛИТЕ, ПОЭТОМУ ТАБЛИЦЫ РАЗЛИЧАЮТСЯ ПО НАПОЛНЕНИЮ.
|
||||
|
||||
ЧТО Я ЗАМЕТИЛ ПРИ СОЗДАНИИ МАТ ПРЕДСТАВЛЕНИЙ В КХ?
|
||||
ТО ЧТО В ГП МАТ ПРЕДСТАВЛЕНИЯ ЭТО ОТДЕЛЬНАЯ ТАБЛИЦА, КОТОРАЯ ХРАНИТ ОПРЕДЕЛЕННЫЙ РЕЗУЛЬТАТ, ОНА НЕ ЯВЛЕТСЯ ДИНАМИЧЕСКОЙ,
|
||||
ЕЕ НЕОБХОДИМО ОБНОВЛЯТЬ ПРИ ПОСТУПЛЕНИИ НОВЫХ ДАННЫХ, СООТВЕТСТВЕННО ВЕСЬ АНАЛИЗ ДАННЫХ ГП ДЕЛАЕТ ПО ВСЕМ ДАННЫМ ЗАПРОСА.
|
||||
КХ В ОТЛИЧИЕ ОТ ГП РАБОТАЕТ ИНАЧЕ, ОН ПОДХВАТЫВАЕТ ЛИШЬ НОВЫЕ ДАННЫЕ, ПРИЧЕМ ЭТИ НОВЫЕ ДАННЫЕ МОГУТ ПОСТУПАТЬ ТОЛЬКО ИЗ ТАБЛИЦЫ ФАКТОВ,
|
||||
ЕСЛИ ОНИ ПОСТУПЯТ ИЗ ТАБЛИЦ ИЗМЕРЕНИЙ, ТО НИКАКИХ РАСЧЕТОМ КХ ДЕЛАТЬ НЕ БУДЕТ.
|
||||
ТАКЖЕ СТОИТ ОТМЕТИТЬ, ЧТО В КХ ПОЯВЛЯЕТСЯ ЦЕЛЕВАЯ ТАБЛИЦА, В КОТОРУЮ МАТ ПРЕДСТАВЛЕНИЕ И ПРОИЗВОДИТ ЗАПИСЬ ДАННЫХ И ПОТОМ
|
||||
В ЗАПРОСЕ МЫ ОБРАЩАЕМСЯ ИМЕННО К ЦЕЛЕВОЙ ТАБЛИЦЕ, А НЕ К МАТ ПРЕДСТАВЛЕНИЮ (МАТ ПРЕДСТАВЛЕНИЕ ЭТО ВСЕГО ЛИШЬ ТРИГГЕР, КОТОРЫЕ УЛАВЛИВАЕТ НОВЫЕ ДАННЫЕ ИЗ ТАБЛИЦЫ ФАКТОВ).
|
||||
Reference in New Issue
Block a user