ClickHouse 命令速查
引擎 / 函数 / 聚合 / 物化视图 · 点击命令复制
引擎 / 函数 / 聚合 / 物化视图 · 点击命令复制
写完建表语句,发现ENGINE = SummingMergeTree() 报错,或者 GROUP BY 里的聚合函数死活不出数——翻官方文档又太慢。这个速查表把 ClickHouse 的二十多种引擎、上百个函数和聚合器按类别摊开,点一下就能看到参数说明和典型写法,不用在英文文档里反复跳转。所有数据都在这页 HTML 里,离线也能查。
运维接手一套日增 50 亿条的埋点集群,建表时在 MergeTree 和 Log 引擎间犹豫:Log 写入快但缺乏主键索引,MergeTree 支持分区但写入吞吐会下降。打开本工具,在引擎分类下逐一比对 Log、StripeLog、TinyLog 的并发限制与数据持久化策略,确认 Log 引擎仅适合批量写入且不支持并发读,最终为实时流选择 ReplacingMergeTree 做去重,为离线归档选 Log 做临时存储,避免上线后因引擎选错导致查询超时。
数据工程师跑日报时发现 `uniqExact` 去重结果比业务系统多出 0.3%,怀疑是 `countDistinct` 与 `uniqCombined` 混用导致精度偏差。在工具聚合函数列表里查 `uniqExact` 和 `uniqCombined` 的算法说明与内存开销,确认前者基于精确哈希表、后者使用 HyperLogLog 近似算法,误差率约 1-2%。对比后把高频查询切为 `uniqCombined(15)` 平衡性能,低频对账保留 `uniqExact`,把误差控制在业务容忍的 0.5‰ 以内。
分析师写 SQL 做用户行为路径分析,需要在 `groupArray` 内按 `event_time` 排序取前 5 个事件,但不确定 ClickHouse 的 `ORDER BY` 子句在 `arraySort` 里的生效范围。在工具函数模块查 `arraySort` 与 `arrayEnumerate` 的语法示例,发现窗口函数 `row_number() over (partition by uid order by ts)` 在 21.3 后才稳定,改用 `arraySlice(arraySort((x, y) -> y.event_time)(groupArray(tuple(event, event_time))), 1, 5)` 实现,避免因版本兼容导致结果错乱。
数仓开发建了一张 `SummingMergeTree` 物化视图用于聚合每日 PV,上线后发现 `count()` 结果比源表多一倍。在工具引擎文档里查 `SummingMergeTree` 的合并规则,发现它对非聚合列(如 `user_id`)会取最后一条而非求和,导致重复计数。修正视图定义,把 `user_id` 移出 `ORDER BY` 并改为 `argMax` 保留最新值,重新物化后数据对齐。
BI 开发写 `toStartOfInterval(time, INTERVAL 1 HOUR)` 时报 `Illegal type of argument`,怀疑是 ClickHouse 版本或参数顺序问题。在工具函数分类里查 `toStartOfInterval` 的签名,确认第二个参数必须是 `Interval` 类型而非整数,且 `INTERVAL 1 HOUR` 需用单引号包裹。把写法改为 `toStartOfInterval(time, INTERVAL '1' HOUR)` 后查询通过,避免在排障上浪费半小时。
| 输入 | 输出 | 说明 |
|---|---|---|
| Sum | sum(x) → 计算数值列的总和。忽略 NULL。例:sum(Salary) → 45000 | 常规:最常用的聚合函数,验证基础聚合能力 |
| MergeTree | MergeTree() → 基础合并树引擎。支持主键排序、分区、TTL。例:ENGINE = MergeTree() ORDER BY id | 常规:最核心的存储引擎,覆盖引擎类查询 |
| count | count() → 返回行数。count(DISTINCT col) → 去重计数。例:count() → 1000 | 常规:无参数与有参数两种调用,验证函数重载 |
| ArrayJoin | arrayJoin(arr) → 将数组展开为多行。例:SELECT arrayJoin([1,2,3]) → 1,2,3(三行) | 边界:表函数而非普通函数,容易与 arrayJoin 混淆,需区分 |
| SummingMergeTree | SummingMergeTree() → 合并时预聚合数值列。例:ENGINE = SummingMergeTree() ORDER BY (date, product_id) | 边界:合并时自动求和,非实时聚合,新手误以为实时查询结果已聚合 |
| groupArray | groupArray(x) → 将分组内的值聚合成数组。例:groupArray(Name) → ['Alice','Bob'] | 易错:结果顺序不保证,依赖插入顺序,需配合 ORDER BY 或 groupArrayInsertAt |
| ReplacingMergeTree | ReplacingMergeTree(version_col) → 合并时按版本列去重。例:ENGINE = ReplacingMergeTree(version) ORDER BY id | 易错:去重仅在合并时发生,查询需加 FINAL 或手动去重,否则返回重复行 |
| quantile | quantile(level)(x) → 计算近似分位数。例:quantile(0.5)(Age) → 35 | 易错:参数顺序为 level 在前,列在后,与普通聚合函数不同,容易写反 |
1.引擎名写错或大小写不对
ENGINE = MergeTreeENGINE = MergeTree()ClickHouse 引擎名严格区分大小写,且多数引擎需要带括号(即使无参数),如 MergeTree()、TinyLog()。漏括号或大小写错误(如 mergetree)会导致建表失败。
2.ORDER BY 字段未出现在主键或表结构中
CREATE TABLE t (a Int32, b String) ENGINE = MergeTree() ORDER BY cCREATE TABLE t (a Int32, b String, c Date) ENGINE = MergeTree() ORDER BY cORDER BY 指定的字段必须在表列中定义,否则报错 'Unknown column'。排序键是物理存储顺序,不能凭空引用。
3.聚合函数里混用非聚合列且无 GROUP BY
SELECT name, count(*) FROM usersSELECT name, count(*) FROM users GROUP BY nameClickHouse 默认不允许 SELECT 中混用聚合列和非聚合列而不加 GROUP BY。除非用 any() 或 max() 等包装非聚合列,否则报错 'not in GROUP BY key'。
4.日期字面量未用单引号包裹
WHERE date = 2023-01-01WHERE date = '2023-01-01'ClickHouse 中日期/时间字面量必须用单引号括起,否则被解析为算术表达式(2023-01-01 = 2021),导致逻辑错误且不报错。
5.字符串比较用了双等号但类型不匹配
WHERE id = 123 -- id 是 String 类型WHERE id = '123'ClickHouse 不会自动将字符串与数字隐式转换,类型不匹配时可能返回空结果或报错。字符串列必须用字符串字面量比较。
6.用 LIMIT 代替 LIMIT BY 做分组取 top N
SELECT * FROM events ORDER BY time DESC LIMIT 10SELECT * FROM events ORDER BY time DESC LIMIT 10 BY category普通 LIMIT 只返回全局前 N 行,而 LIMIT BY 可对每个分组独立取前 N 行。误用普通 LIMIT 会导致只看到某几个分组的数据。
7.FINAL 修饰符误用在非 MergeTree 表上
SELECT * FROM tiny_log_table FINALSELECT * FROM tiny_log_table -- 或改用 MergeTree 表FINAL 仅对 MergeTree 系列表有效,用于合并分区内的重复行。对 TinyLog、Memory 等表使用会报错 'Table doesn't support FINAL'。
count(DISTINCT col) = 去重后的行数
col待去重的列名或表达式表 orders 有 100 行,user_id 列含 3 个重复值:count(DISTINCT user_id) = 97,即 100 − 3 = 97 个不同用户。
MergeTree 是基础引擎,只管按主键排序后合并分区,不处理重复行。ReplacingMergeTree 在合并时,会按 ORDER BY 定义的排序键去重,只保留每个排序键的最后一行(按插入时间或版本字段)。如果你需要实时去重查询,ReplacingMergeTree 不是实时的——合并是后台异步做的,查询时仍可能见到重复,得配合 FINAL 修饰符或聚合函数。
ClickHouse 是 LSM 树架构,插入的数据先写内存再异步合并。合并前,同一个分区里会存在多个版本的数据段。如果你用的是 MergeTree 而非去重引擎,重复是正常现象。解决方案:1)换 ReplacingMergeTree 或 CollapsingMergeTree;2)查询加 FINAL 关键字(性能较差);3)定期执行 OPTIMIZE TABLE FINAL 强制合并。注意 OPTIMIZE 是重量级操作,避免高频执行。
uniq 返回近似去重计数,基于 HyperLogLog 算法,误差约 1%-2%,但内存和速度极优,适合大基数去重。uniqExact 返回精确去重计数,用哈希表实现,内存随基数线性增长,大数据量下容易 OOM。日常看 UV 用 uniq 足够;只有需要精确对账或基数很小(百万以内)时才用 uniqExact。本工具在函数说明里标注了每个聚合函数的精度和适用场景。
ClickHouse 默认单查询内存上限由 max_memory_usage 控制(通常 10GB)。嵌套子查询常因中间结果全量驻内存触发该限制。优化方向:1)把子查询结果先物化成临时表;2)用 global in / join 替代子查询;3)加 LIMIT 或 WHERE 提前过滤;4)调大 max_memory_usage(注意影响其他查询)。本工具在“常见错误”区提供了各限制参数的默认值和建议调整范围。
ClickHouse 的主键索引不是传统 B+ 树,而是稀疏索引——只记录每 8192 行的主键值。走索引的前提是 WHERE 条件里包含主键的前缀列。比如主键是 (date, city),WHERE date='2024-01-01' 能走索引,WHERE city='Beijing' 不走。另外,使用函数包裹主键列也会失效,比如 WHERE toDate(timestamp)='2024-01-01' 不走,应写成 WHERE timestamp >= '2024-01-01 00:00:00' AND timestamp < '2024-01-02 00:00:00'。
不一样。MySQL 默认允许 SELECT 列不在 GROUP BY 中(但结果不确定),ClickHouse 严格按 SQL 标准——SELECT 列必须是分组键或聚合函数。另外,ClickHouse 的 GROUP BY 支持 WITH TOTALS(生成总计行)和 WITH ROLLUP / CUBE,MySQL 对应的是 WITH ROLLUP 但无 CUBE。性能上 ClickHouse 对高基数 GROUP BY 更友好(万级以上分组),MySQL 在低基数下更灵活。
可能是版本差异。ClickHouse 部分函数在不同版本中名称或行为有变,比如 arrayEnumerate 在旧版叫 arrayEnumerateUniq。也可能你复制的示例用了非标准函数(如某些社区自定义函数)。本工具的示例基于 ClickHouse LTS 23.x 版本,如果你用的版本更低或更高,建议先查对应版本的官方文档。另外注意函数大小写——ClickHouse 函数名不区分大小写,但参数类型必须匹配。
概念类似但实现差异大。MySQL 分区是物理上的数据文件拆分,支持 RANGE、LIST、HASH、KEY 四种模式。ClickHouse 分区更多是逻辑上的目录划分,用于加速分区裁剪和删除旧数据,常用 toYYYYMM(date) 按月分区。ClickHouse 不支持跨分区主键唯一约束,也不支持分区内自动去重。本工具在“引擎对比”表里列出了 ClickHouse 与 MySQL 分区特性的详细对照。
隐私保证所有计算与处理均在你的浏览器本地完成,输入数据不会上传服务器,也不会保存或共享。