Работа над ошибками: 'Синтаксис'.

Практика: 'JOIN' - 'Массивы, Map и вложенные структуры*' - 'Партиции' - 'Индексы'.
This commit is contained in:
mnoskov
2026-05-18 16:55:22 +03:00
parent 80d333fe52
commit ae208a76fb
8 changed files with 577 additions and 0 deletions
@@ -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;
-- Че то кликхаус странно себя ведет, то таблицы сами очищаются, то удаленные таблицы воскресают.
@@ -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;
@@ -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.
@@ -0,0 +1,215 @@
-- Задание 1
CREATE TABLE mnoskov.users
ON CLUSTER clickhouse_cluster
(
user_id UInt32,
name String,
age UInt8,
country String,
is_active Bool
)
ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/mnoskov/users',
'{replica}'
)
ORDER BY user_id;
INSERT INTO mnoskov.users
(user_id,name,age,country,is_active)
VALUES
(1,'Ivan',30,'RU',1),
(2,'Anna',25,'RU',1),
(3,'Petr',45,'RU',0),
(4,'Olga',35,'RU',1),
(5,'John',55,'US',1),
(6,'Alice',42,'US',0),
(7,'Tom',38,'US',1),
(8,'Lucas',28,'BR',1),
(9,'Maria',60,'BR',1),
(10,'Joao',27,'BR',0),
(11,'Kenji',33,'JP',1),
(12,'Yuki',29,'JP',0),
(13,'Chen',40,'CN',1),
(14,'Li',48,'CN',1),
(15,'Fatima',31,'AE',1),
(16,'Ahmed',36,'AE',1),
(17,'George',24,'US',1),
(18,'Nina',34,'RU',1),
(19,'Max',50,'DE',1),
(20,'Sophie',22,'DE',0);
SELECT * FROM mnoskov.users;
--1
SELECT
user_id,
age,
is_active
FROM
mnoskov.users
WHERE
is_active = TRUE
ORDER BY
age DESC
LIMIT 10;
--2
SELECT
name,
country
FROM
mnoskov.users
WHERE
country = 'RU'
ORDER BY
name;
--3
SELECT
user_id,
age,
is_active
FROM
mnoskov.users
WHERE
is_active = TRUE
LIMIT 10;
-- Данные вывелить так как лежат на диске.
--4
SELECT
country,
count() as cnt_users
FROM
mnoskov.users
GROUP BY
country;
--5
SELECT
country,
min(age) AS min_age,
max(age) AS max_age
FROM
mnoskov.users
GROUP BY
country;
--6
SELECT
country,
count() AS cnt_users
FROM
mnoskov.users
GROUP BY
country
ORDER BY
cnt_users DESC;
--Доп
SELECT
uniqExact(country),
uniq(country)
FROM
mnoskov.users;
-- uniqExact(country) - кол-во уникальных значений в колонке country. Но uniq работает также.
-- В инете написано что uniq быстрее.
-- Задание 2
CREATE TABLE mnoskov.events1
ON CLUSTER clickhouse_cluster
(
user_id UInt32,
event_time DateTime,
event_type String
)
ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/mnoskov/events1',
'{replica}'
)
ORDER BY(user_id,event_time);
INSERT INTO mnoskov.events1
(user_id,event_time,event_type)
VALUES
(1,'2025-07-17 09:00:00','click'),
(1,'2025-07-17 09:05:00','view'),
(1,'2025-07-17 09:10:00','purchase'),
(2,'2025-07-17 10:00:00','click'),
(2,'2025-07-17 10:05:00','click'),
(2,'2025-07-17 11:00:00','view'),
(3,'2025-07-16 12:00:00','view'),
(3,'2025-07-16 12:30:00','click'),
(3,'2025-07-17 08:00:00','purchase'),
(4,'2025-07-17 13:00:00','click'),
(4,'2025-07-17 14:00:00','view'),
(4,'2025-07-17 15:00:00','view'),
(5,'2025-07-16 20:00:00','view'),
(5,'2025-07-17 01:00:00','click'),
(5,'2025-07-17 06:00:00','purchase'),
(6,'2025-07-17 07:30:00','view'),
(6,'2025-07-17 08:45:00','click'),
(7,'2025-07-16 23:59:59','click'),
(7,'2025-07-17 00:01:00','purchase'),
(7,'2025-07-17 10:10:00','view');
SELECT * FROM mnoskov.events1;
--1
SELECT
user_id,
event_time,
event_type,
count() OVER(PARTITION BY user_id ORDER BY event_time) AS com_users
FROM
mnoskov.events1;
--2
SELECT
user_id,
event_time,
event_type,
row_number() OVER (PARTITION BY user_id ORDER BY event_time) rn,
dense_rank() OVER (PARTITION BY user_id ORDER BY event_time) dr,
count() OVER(PARTITION BY user_id ORDER BY event_time) AS c,
sum(1) OVER(PARTITION BY user_id ORDER BY event_time) AS s
FROM
mnoskov.events1;
--3
SELECT
user_id,
event_time,
event_type,
count() OVER(PARTITION BY user_id ORDER BY event_time ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS com_users,
count() OVER(PARTITION BY user_id ORDER BY event_time ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS com_users2
FROM
mnoskov.events1;
-- все четенько работает, никаких ошибкок нет
--4-5-6
WITH recent_events AS (
SELECT
user_id,
event_time,
event_type
FROM
mnoskov.events1
WHERE
event_time < now() - INTERVAL 1 DAY
)
SELECT
user_id,
count(*) AS cnt_user_actions
FROM
recent_events
GROUP BY
user_id
ORDER BY
cnt_user_actions DESC
LIMIT
5;
-- не понял, как сделать 6 пункт.
-- Ответ на вопрос - ну да - поддерживает.
@@ -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, тк нужно вывести имя для каждого лога а не юзера.
@@ -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;
@@ -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');
@@ -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 Индекс не будет использоваться.