diff --git a/mnoskov/clickhouse/Практика clickhouse/1. Таблицы и типы данных/1. Движки/1. Семейство MergeTree/practice_table_types.sql b/mnoskov/clickhouse/Практика clickhouse/1. Таблицы и типы данных/1. Движки/1. Семейство MergeTree/practice_table_types.sql new file mode 100644 index 0000000..6140c2f --- /dev/null +++ b/mnoskov/clickhouse/Практика clickhouse/1. Таблицы и типы данных/1. Движки/1. Семейство MergeTree/practice_table_types.sql @@ -0,0 +1,120 @@ +-- Задание 1 + +CREATE TABLE mnoskov.page_views +ON CLUSTER clickhouse_cluster +( + user_id UInt64, + url String, + view_time DateTime +) +ENGINE = MergeTree +PARTITION BY toYYYYMM(view_time) +ORDER BY (user_id, view_time); + + +-- Задание 2 +CREATE TABLE mnoskov.users +ON CLUSTER clickhouse_cluster +( + id UInt64, + name String, + update_at DateTime +) +ENGINE = ReplacingMergeTree(update_at) +ORDER BY id; + + +-- Задание 3 +CREATE TABLE mnoskov.region_sales +ON CLUSTER clickhouse_cluster +( + region String, + date Date, + sales UInt64 +) +ENGINE = SummingMergeTree +PARTITION BY toYYYYMM(date) +ORDER BY (region, date); + +INSERT INTO mnoskov.region_sales (region, date, sales) +VALUES + ('Moscow', '2026-05-01', 100), + ('Moscow', '2026-05-01', 150), + ('Moscow', '2026-05-01', 200), + ('Berlin', '2026-05-02', 300), + ('Berlin', '2026-05-02', 120), + ('Berlin', '2026-05-02', 80), + ('Paris', '2026-05-03', 500), + ('Paris', '2026-05-03', 250), + ('London', '2026-05-04', 400), + ('Tokyo', '2026-05-05', 700); + +SELECT * FROM mnoskov.region_sales; +-- Все четко работает. + + +-- Задание 4 +CREATE TABLE mnoskov.user_metrics +ON CLUSTER clickhouse_cluster +( + region String, + avg_age_state AggregateFunction(avg, UInt8), + active_users_state AggregateFunction(uniq, UInt64) +) +ENGINE = AggregatingMergeTree +ORDER BY region; + +CREATE TABLE mnoskov.user_metrics_source +ON CLUSTER clickhouse_cluster +( + region String, + age UInt8, + user_id UInt64 +) +ENGINE = MergeTree +ORDER BY region; + +INSERT INTO mnoskov.user_metrics_source +(region, age, user_id) +VALUES + ('Moscow', 25, 1001), + ('Moscow', 30, 1002), + ('Moscow', 22, 1003), + ('Moscow', 25, 1001), + ('Berlin', 40, 2001), + ('Berlin', 35, 2002), + ('Berlin', 29, 2003), + ('Berlin', 35, 2002), + ('Paris', 31, 3001), + ('Paris', 28, 3002), + ('Paris', 45, 3003), + ('Paris', 31, 3001), + ('London', 50, 4001), + ('London', 41, 4002), + ('London', 38, 4003), + ('London', 41, 4002), + ('Tokyo', 27, 5001), + ('Tokyo', 33, 5002), + ('Tokyo', 29, 5003), + ('Tokyo', 33, 5002); +SELECT * FROM mnoskov.user_metrics_source; + +INSERT INTO mnoskov.user_metrics +SELECT + region, + avgState(age) AS avg_age_state, + uniqState(user_id) AS active_users_state +FROM + mnoskov.user_metrics_source +GROUP BY + region; + +SELECT + region, + avgMerge(avg_age_state), + uniqMerge(active_users_state) +FROM + mnoskov.user_metrics +GROUP BY + region; +-- Че то кликхаус странно себя ведет, то таблицы сами очищаются, то удаленные таблицы воскресают. \ No newline at end of file diff --git a/mnoskov/clickhouse/Практика clickhouse/1. Таблицы и типы данных/1. Движки/2. View и Distribute движки/practice_view_and_distribute_engines.sql b/mnoskov/clickhouse/Практика clickhouse/1. Таблицы и типы данных/1. Движки/2. View и Distribute движки/practice_view_and_distribute_engines.sql new file mode 100644 index 0000000..2a7de23 --- /dev/null +++ b/mnoskov/clickhouse/Практика clickhouse/1. Таблицы и типы данных/1. Движки/2. View и Distribute движки/practice_view_and_distribute_engines.sql @@ -0,0 +1,86 @@ +-- Задание 1 + +CREATE TABLE mnoskov.logs +ON CLUSTER clickhouse_cluster ( + dt DateTime, + user_id UInt32, + event_type String +) +ENGINE = ReplicatedMergeTree( + '/clickhouse/tables/{shard}/mnoskov/logs', + '{replica}' +) +ORDER BY dt; + +CREATE TABLE mnoskov.dist_logs +ON CLUSTER clickhouse_cluster +ENGINE = Distributed( + 'clickhouse_cluster', + 'mnoskov', + 'logs', + user_id +); +-- параметр rand() - это ключ распределния между шардами. Если стоит он, то распределяются данные случайно, если укащать колонку, то по колонке. + + +-- Задание 2 + +CREATE TABLE mnoskov.events +ON CLUSTER clickhouse_cluster +( + user_id UInt32, + event_type String, + ts DateTime +) +ENGINE = ReplicatedMergeTree( + '/clickhouse/tables/{shard}/mnoskov/events', + '{replica}' +) +ORDER BY (ts, user_id); + +CREATE VIEW mnoskov.top_active_users AS +SELECT + user_id, + count(*) AS cnt_user +FROM + mnoskov.events +GROUP BY + user_id +ORDER BY + cnt_user DESC, user_id +LIMIT 100; +-- VIEW не подойдет, тк вьюшка будет каждый раз считаться заново, а это энергозатратно, поэтому надо использовать MATERIALIZED VIEW. + + +-- Задание 3 + +CREATE TABLE mnoskov.raw_events +ON CLUSTER clickhouse_cluster +( + event_time DateTime, + user_id UInt32, + event_type String +) +ENGINE=MergeTree +ORDER BY(event_time,user_id); + +CREATE TABLE mnoskov.agr_clicks_per_day +ON CLUSTER clickhouse_cluster +( + event_date Date, + count UInt64 +) +ENGINE=SummingMergeTree +ORDER BY event_date; + +CREATE MATERIALIZED VIEW mnoskov.mv_clicks_per_day +ON CLUSTER clickhouse_cluster +TO mnoskov.agr_clicks_per_day AS +SELECT + toDate(event_time) AS event_date, + count(*) AS count +FROM mnoskov.raw_events +GROUP BY event_date; + +SELECT * FROM mnoskov.raw_events; +SELECT * FROM mnoskov.agr_clicks_per_day; \ No newline at end of file diff --git a/mnoskov/clickhouse/Практика clickhouse/1. Таблицы и типы данных/2. Типы данных/practice_data_types.sql b/mnoskov/clickhouse/Практика clickhouse/1. Таблицы и типы данных/2. Типы данных/practice_data_types.sql new file mode 100644 index 0000000..b8d8218 --- /dev/null +++ b/mnoskov/clickhouse/Практика clickhouse/1. Таблицы и типы данных/2. Типы данных/practice_data_types.sql @@ -0,0 +1,115 @@ +-- Задание 1 + +CREATE TABLE mnoskov.raw_data1 +ON CLUSTER clickhouse_cluster ( + user_id String, + balance String, + created_at String +) +ENGINE = ReplicatedMergeTree( + '/clickhouse/tables/{shard}/mnoskov/raw_data1', + '{replica}' +) +ORDER BY tuple(); + +SELECT + user_id, + balance, + created_at +FROM ( + SELECT + user_id, + toDecimal64OrNull(balance,2) AS balance, + toDateTime64OrNull(created_at, 3) AS created_at +FROM + mnoskov.raw_data1 +) +WHERE + balance IS NOT NULL AND created_at IS NOT NULL; + + +-- Задание 2 + +CREATE TABLE mnoskov.events +ON CLUSTER clickhouse_cluster ( + event_id String, + event_date String, -- форматы: "2023-01-01", "01.02.2023", "invalid" + event_time String -- форматы: "12:30:45", "25:99:99" +) +ENGINE = ReplicatedMergeTree( + '/clickhouse/tables/{shard}/mnoskov/events', + '{replica}' +) +ORDER BY + event_id; + +SELECT * FROM mnoskov.events; + + +SELECT + event_id, + event_datetime +FROM ( + SELECT + event_id, + parseDateTimeBestEffortOrNull(concat(event_date, ' ', event_time)) AS event_datetime + FROM + mnoskov.events +) +ORDER BY + event_datetime; + + +-- Задание 3 + +CREATE TABLE mnoskov.user_activity +ON CLUSTER clickhouse_cluster +( + user_id UInt32, + actions String -- JSON: ["login", "purchase", "logout"] +) +ENGINE = ReplicatedMergeTree( + '/clickhouse/tables/{shard}/mnoskov/user_activity', + '{replica}' +) +ORDER BY + user_id; + +SELECT + user_id, + count(*) as user_actions_cnt +FROM + mnoskov.user_activity +WHERE + has(JSONExtract(actions, 'Array(String)'), 'purchase') +GROUP BY + user_id +ORDER BY + user_id; + + +-- Задание 4 + +CREATE TABLE mnoskov.sensor_data +ON CLUSTER clickhouse_cluster +( + sensor_id UInt32, + value Float64, + status Enum('ok' = 1, 'error' = 2) +) +ENGINE = ReplicatedMergeTree( + '/clickhouse/tables/{shard}/mnoskov/sensor_data', + '{replica}' +) +ORDER BY sensor_id; + +--1-2. +SELECT + toString(status) AS str_status, + avg(value) AS avg_value +FROM + mnoskov.sensor_data +GROUP BY + str_status; +--3 +-- не понял задание. все строки в value можно преобразовать в Decimal(10,2), потому что value - Float64, а он все null переделывает в 0. \ No newline at end of file diff --git a/mnoskov/clickhouse/практика clickhouse/2. Запросы/Синтаксис/practice_syntax.sql b/mnoskov/clickhouse/Практика clickhouse/2. Запросы/1. Синтаксис/practice_syntax.sql similarity index 100% rename from mnoskov/clickhouse/практика clickhouse/2. Запросы/Синтаксис/practice_syntax.sql rename to mnoskov/clickhouse/Практика clickhouse/2. Запросы/1. Синтаксис/practice_syntax.sql diff --git a/mnoskov/clickhouse/Практика clickhouse/2. Запросы/2. JOIN/practice_join.sql b/mnoskov/clickhouse/Практика clickhouse/2. Запросы/2. JOIN/practice_join.sql new file mode 100644 index 0000000..f29dbc3 --- /dev/null +++ b/mnoskov/clickhouse/Практика clickhouse/2. Запросы/2. JOIN/practice_join.sql @@ -0,0 +1,57 @@ +-- Задание 1 + +SELECT + pro.product_id, + pro.name, + pri.price +FROM + mnoskov.products pro +ANY LEFT JOIN + mnoskov.prices pri ON pro.product_id = pri.product_id; +-- 2 Выберется случайная из цен. +-- 3 ALL LEFT JOIN - работает также как и LEFT JOIN. Его избегают потому что он тянет больше строк в оперативную память. + + +-- Задание 2 + +SELECT + o.time, + o.symbol, + o.size, + t.price +FROM + mnoskov.orders o +ASOF JOIN + mnoskov.ticks t ON o.symbol = t.symbol AND t.time <= o.time +WHERE symbol = 'AAPL'; +-- Потому что дата сделки и дата последней фиксации цены - разные. + + +-- Задание 3 + +SELECT + user_id, + name +FROM + mnoskov.users +WHERE user_id IN ( + SELECT user_id + FROM mnoskov.payments + WHERE amount > 1000 +); +-- 3 Потому что это фильтрация по существованию. Как оптимизируется не знаю, наверное как подзапрос. + + +-- Задание 4 + +SELECT + bl.user_id, + u.name AS user_name, + bl.event_time, + bl.event_type +FROM + mnoskov.big_logs bl +JOIN + mnoskov.users u ON bl.user_id = u.user_id; +-- 2 потому что таблица users меньше, а значит в оператику полетит меньше данных. +-- 3 если user_id уникален, то нужно выбирать ALL, так как дублей нет. В текущем задании дубли есть, но мы все равно выбираем ALL, тк нужно вывести имя для каждого лога а не юзера. \ No newline at end of file diff --git a/mnoskov/clickhouse/Практика clickhouse/2. Запросы/3. Массивы, Map и вложенные структуры*/practice_arrays_maps_and_nested_structures.sql b/mnoskov/clickhouse/Практика clickhouse/2. Запросы/3. Массивы, Map и вложенные структуры*/practice_arrays_maps_and_nested_structures.sql new file mode 100644 index 0000000..6ed16cc --- /dev/null +++ b/mnoskov/clickhouse/Практика clickhouse/2. Запросы/3. Массивы, Map и вложенные структуры*/practice_arrays_maps_and_nested_structures.sql @@ -0,0 +1,44 @@ +-- Задание 1 + +--1-2 +SELECT + user_id, + tags, + indexOf(tags, 'promo') AS pos_promo +FROM + mnoskov.events +WHERE + has(tags, 'promo'); + +--3 +SELECT + user_id, + arrayJoin(tags) AS tag +FROM + mnoskov.events; + + +-- Задание 2 + +--1 +SELECT user_id, scores['math'] FROM mnoskov.metrics; + +--2 +SELECT user_id, mapValues(scores) FROM mnoskov.metrics; + + +-- Заданик 3 + +--1 +SELECT * FROM mnoskov.users_nested WHERE has(contacts.type, 'email'); + +--2 +SELECT + id, + name, + c.type, + c.value +FROM + mnoskov.users_nested +ARRAY JOIN + contacts AS c; \ No newline at end of file diff --git a/mnoskov/clickhouse/Практика clickhouse/3. Индексы и сжатие/1. Партиции/practice_partitions.sql b/mnoskov/clickhouse/Практика clickhouse/3. Индексы и сжатие/1. Партиции/practice_partitions.sql new file mode 100644 index 0000000..2a855a6 --- /dev/null +++ b/mnoskov/clickhouse/Практика clickhouse/3. Индексы и сжатие/1. Партиции/practice_partitions.sql @@ -0,0 +1,86 @@ +-- Задание 1 + +CREATE TABLE mnoskov.sales +ON CLUSTER clickhouse_cluster +( + id UInt64, + region String, + sale_date Date, + amount Float64 +) +ENGINE=ReplicatedMergeTree( + '/clickhouse/tables/{shard}/mnoskov/sales', + '{replica}' +) +PARTITION BY toYYYYMM(sale_date) +ORDER BY sale_date; + +ALTER TABLE mnoskov.sales DROP PARTITION '202405'; +SELECT * FROM mnoskov.sales; +-- Пропали строки где год/месяц 202405. +-- Если не указывать PARTITION BY, то строки бы искались по всей таблице, что дольше и затранее. + + +-- Задание 2 + +CREATE TABLE mnoskov.events +ON CLUSTER clickhouse_cluster +( + id UInt64, + event_type String, + event_date Date +) +ENGINE=ReplicatedMergeTree( + '/clickhouse/tables/{shard}/mnoskov/events_partitioned', + '{replica}' +) +PARTITION BY event_type +ORDER BY event_date; + +ALTER TABLE mnoskov.events DROP PARTITION 'click'; +ELECT * FROM mnoskov.events; +-- Такой подход опасен, тк например login выполняется не часто по сравнению с click и получается жесткий перевес. + + +-- Задание 3 + +CREATE TABLE mnoskov.logs +ON CLUSTER clickhouse_cluster +( + id UInt64, + user_id UInt64, + log_time DateTime +) +ENGINE=ReplicatedMergeTree( + '/clickhouse/tables/{shard}/mnoskov/logs_partitioned', + '{replica}' +) +PARTITION BY cityHash64(user_id) % 100 +ORDER BY log_time; + +SELECT * FROM mnoskov.logs + +-- 2 с точки зрения баланса, у нас юзеры распределные равномерно и нет никаких перекосов. +-- 3 хз если честно, мне кажется должны ускорять. + + +-- Задание 4 + +CREATE TABLE mnoskov.visits +ON CLUSTER clickhouse_cluster +( + id UInt64, + region String, + visit_date Date +) +ENGINE=ReplicatedMergeTree( + '/clickhouse/tables/{shard}/mnoskov/visits', + '{replica}' +) +PARTITION BY (region, toYYYYMM(visit_date)) +ORDER BY visit_date; + +SELECT * FROM mnoskov.visits; +-- 2 доступа к системным таблицам нет, потэтому я не могу посмотреть имя партиции. +-- 3 ну да +ALTER TABLE mnoskov.visits DROP PARTITION ('RU', '202407'); \ No newline at end of file diff --git a/mnoskov/clickhouse/Практика clickhouse/3. Индексы и сжатие/2. Индексы/practice_indexes.sql b/mnoskov/clickhouse/Практика clickhouse/3. Индексы и сжатие/2. Индексы/practice_indexes.sql new file mode 100644 index 0000000..7147e3e --- /dev/null +++ b/mnoskov/clickhouse/Практика clickhouse/3. Индексы и сжатие/2. Индексы/practice_indexes.sql @@ -0,0 +1,69 @@ +-- Задание 1 + +CREATE TABLE mnoskov.logs +ON CLUSTER clickhouse_cluster +( + log_id UInt64, + ts DateTime +) +ENGINE=ReplicatedMergeTree( + '/clickhouse/tables/{shard}/mnoskov/logs_ttl', + '{replica}' +) +ORDER BY ts; +ALTER TABLE mnoskov.logs ADD INDEX idx_ts ts TYPE minmax granularity 2; + +SELECT * FROM logs WHERE ts BETWEEN '2024-06-02' AND '2024-06-03'; + +-- 2 У меня нет доступа к системным таблицам, я не могу посмотреть. +-- Если в order by будет log_id а не ts, то индекс не будет работать. + + +-- Задание 2 + +CREATE TABLE mnoskov.clicks +ON CLUSTER clickhouse_cluster +( + id UInt64, + country String +) +ENGINE=ReplicatedMergeTree( + '/clickhouse/tables/{shard}/mnoskov/clicks', + '{replica}' +) +ORDER BY id; + +ALTER TABLE mnoskov.clicks ADD INDEX idx_country country TYPE set(1000) GRANULARITY 2; +ALTER TABLE mnoskov.clicks DROP INDEX idx_country; + +explain +SELECT * FROM clicks WHERE country = 'RU'; +-- 3 не использутся индеск, я туда 11тыс строк заинсертил, все равно не используется. +-- 4 я думаю индекс не будет работать эффективно, а может быть и вообще будет запрос замедлять. + + +-- Задание 3 + +CREATE TABLE mnoskov.users +ON CLUSTER clickhouse_cluster +( + id UInt64, + email String +) +ENGINE=ReplicatedMergeTree( + '/clickhouse/tables/{shard}/mnoskov/users_simple', + '{replica}' +) +ORDER BY id; + +ALTER TABLE mnoskov.users ADD INDEX idx_email email TYPE bloom_filter(0.01) GRANULARITY 2; + +explain +SELECT * FROM users WHERE email = 'alice@mail.com'; +-- 2 Ну я не вижу в EXPLAIN какое либо упоминание индекса, но я ссылаюсь на то, что просто мало данных в таблице. + +-- 3 Потому что этот индекс работает только на точные совпадения - exact match. + +explain +SELECT * FROM users WHERE email LIKE 'alice%'; +-- 4 Индекс не будет использоваться. \ No newline at end of file