284 lines
11 KiB
SQL
284 lines
11 KiB
SQL
Этап 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. Тестирование и валидация
|
||
В ДАННОМ ЗАДАНИИ Я НЕ ПРОИЗВОДИЛ СРАВНЕНИЕ ТАБЛИЦ ИЗ ГП И КХ, ТАК КАК ТАКБЛИЦЫ
|
||
С ОДИНАКОВЫМИ НАЗВАНИЯМИ ПРОСТО НАПРОСТО СЧИТАЮТ РАЗНЫЕ ПОКАЗАТЕЛИ.
|
||
ПОЧЕМУ ТАК ПРОИЗОШЛО? ПОТОМУ ЧТО В ЗАДАНИИ КХ ДАНЫ СОВСЕМ ДРУГИЕ ПОКАЗАЛИТЕ, ПОЭТОМУ ТАБЛИЦЫ РАЗЛИЧАЮТСЯ ПО НАПОЛНЕНИЮ.
|
||
|
||
ЧТО Я ЗАМЕТИЛ ПРИ СОЗДАНИИ МАТ ПРЕДСТАВЛЕНИЙ В КХ?
|
||
ТО ЧТО В ГП МАТ ПРЕДСТАВЛЕНИЯ ЭТО ОТДЕЛЬНАЯ ТАБЛИЦА, КОТОРАЯ ХРАНИТ ОПРЕДЕЛЕННЫЙ РЕЗУЛЬТАТ, ОНА НЕ ЯВЛЕТСЯ ДИНАМИЧЕСКОЙ,
|
||
ЕЕ НЕОБХОДИМО ОБНОВЛЯТЬ ПРИ ПОСТУПЛЕНИИ НОВЫХ ДАННЫХ, СООТВЕТСТВЕННО ВЕСЬ АНАЛИЗ ДАННЫХ ГП ДЕЛАЕТ ПО ВСЕМ ДАННЫМ ЗАПРОСА.
|
||
КХ В ОТЛИЧИЕ ОТ ГП РАБОТАЕТ ИНАЧЕ, ОН ПОДХВАТЫВАЕТ ЛИШЬ НОВЫЕ ДАННЫЕ, ПРИЧЕМ ЭТИ НОВЫЕ ДАННЫЕ МОГУТ ПОСТУПАТЬ ТОЛЬКО ИЗ ТАБЛИЦЫ ФАКТОВ,
|
||
ЕСЛИ ОНИ ПОСТУПЯТ ИЗ ТАБЛИЦ ИЗМЕРЕНИЙ, ТО НИКАКИХ РАСЧЕТОМ КХ ДЕЛАТЬ НЕ БУДЕТ.
|
||
ТАКЖЕ СТОИТ ОТМЕТИТЬ, ЧТО В КХ ПОЯВЛЯЕТСЯ ЦЕЛЕВАЯ ТАБЛИЦА, В КОТОРУЮ МАТ ПРЕДСТАВЛЕНИЕ И ПРОИЗВОДИТ ЗАПИСЬ ДАННЫХ И ПОТОМ
|
||
В ЗАПРОСЕ МЫ ОБРАЩАЕМСЯ ИМЕННО К ЦЕЛЕВОЙ ТАБЛИЦЕ, А НЕ К МАТ ПРЕДСТАВЛЕНИЮ (МАТ ПРЕДСТАВЛЕНИЕ ЭТО ВСЕГО ЛИШЬ ТРИГГЕР, КОТОРЫЕ УЛАВЛИВАЕТ НОВЫЕ ДАННЫЕ ИЗ ТАБЛИЦЫ ФАКТОВ).
|