ClickHouse
2025年9月5日大约 2 分钟
注意时区
Hello World
你这个问题非常关键,关系到 ClickHouse 的分区设计理念:
为什么用表达式 PARTITION BY concat(...)
,而不是单独新增一个“分区字段”?
1. ClickHouse 分区不是逻辑列,而是物理划分
- ClickHouse 的分区是基于表达式计算的结果来物理划分存储数据的目录,它不是表中的普通列,而是建表时通过表达式“派生”出来的。
- 这意味着不需要额外保存一个“分区字段”列,因为分区信息是根据已有的字段(比如
outtime
)计算得出。
2. 直接用时间字段计算分区,避免数据冗余
如果你额外添加一个专门的“分区字段”,
- 会导致数据重复存储这部分信息,浪费存储空间
- 增加写入时数据的复杂度和错误风险(分区字段要和时间字段同步)
直接用
outtime
字段计算分区表达式,能保证分区自动、准确。
3. 灵活且便于变更分区策略
- 通过表达式控制分区,修改分区粒度只需修改建表语句中的表达式即可,无需修改表结构。
- 例如想改成按月分区,只改
PARTITION BY toYYYYMM(outtime)
,不需要加列。
4. 查询性能优化
- 分区表达式是 ClickHouse 用来做数据裁剪(pruning)的依据,直接用时间字段计算,查询时按时间范围过滤,可以高效跳过无关分区。
- 额外字段不一定能带来更好的分区裁剪效果。
总结
方式 | 说明 | 推荐度 |
---|---|---|
用表达式 PARTITION BY ... | 分区由字段直接计算,物理划分,零冗余 | ✅ 最常见推荐方式 |
新增分区字段列 | 需要维护字段一致性,数据冗余,复杂 | ❌ 不建议 |
删除重复数据
CREATE TABLE t_access_person_record_dedup AS
SELECT *
FROM (
SELECT *,
row_number() OVER (PARTITION BY access_person_record_id ORDER BY access_time DESC) AS rn
FROM t_access_person_record
)
WHERE rn = 1;
DROP TABLE t_access_person_record;
RENAME TABLE t_access_person_record_dedup TO t_access_person_record;