本文整理 ClickHouse 日常开发中常用的 SQL 语法,适合作为速查手册或博客笔记。ClickHouse 是一款面向在线分析(OLAP)的列式数据库管理系统,擅长海量数据的快速聚合查询。
目录
- 连接与基础操作
- 建表与 MergeTree 引擎
- 数据插入
- SELECT 查询
- 聚合与分组
- JOIN 查询
- 数据更新与删除
- 表结构修改
- 物化视图
- 常用函数
- 查询性能优化
- 分布式表
- 总结## 1. 连接与基础操作
命令行连接# 使用 clickhouse-client 连接
bash
clickhouse-client --host=主机 --port=9000 --user=用户名 --password=密码
在容器内执行
bash
docker exec -it clickhouse-server clickhouse-client --query "SELECT version()"
sql
查看库与表
sql
-- 查看所有数据库
SHOW DATABASES;
-- 使用数据库
USE mydb;
-- 查看当前库所有表
SHOW TABLES;
-- 查看表结构
DESC my_table;
2. 建表与 MergeTree 引擎
ClickHouse 最核心的表引擎是 MergeTree,它按 ORDER BY 排序数据,形成稀疏主键索引,让查询能快速跳过无关数据块。
sql
基本建表语法
sql
CREATE TABLE [IF NOT EXISTS] [db.]table_name
(
column1 Type1,
column2 Type2,
...
)
ENGINE = MergeTree()
PARTITION BY 分区表达式
ORDER BY 排序表达式
实际示例
sql
CREATE TABLE wikistat
(
`time` DateTime,
`project` String,
`subproject` String,
`path` String,
`hits` UInt64
)
ENGINE = MergeTree
ORDER BY (time);
sql
CREATE TABLE uk_price_paid
(
price UInt32,
date Date,
postcode1 LowCardinality(String),
postcode2 LowCardinality(String),
type Enum8('terraced' = 1, 'semi-detached' = 2, 'detached' = 3, 'flat' = 4),
is_new UInt8,
duration Enum8('freehold' = 1, 'leasehold' = 2),
addr1 String,
addr2 String,
town LowCardinality(String),
county LowCardinality(String)
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(date)
ORDER BY (postcode1, postcode2, addr1, addr2);
LowCardinality(String) 用于唯一值较少的列,内部使用字典编码,可显著减少存储并提升分组过滤性能。
sql
个关键子句
子句 作用
ORDER BY 每个分片内按物理顺序排序数据,决定主键。必需。
PARTITION BY 将数据拆分为分区,通常按日期范围。不同分区中的 parts 不会合并,实现快速分区裁剪。
PRIMARY KEY 默认与 ORDER BY 相同,稀疏索引基于它构建。
sql
通过 SELECT 建表
sql
CREATE TABLE target_table
ENGINE = MergeTree()
ORDER BY id
AS SELECT * FROM source_table WHERE 1 = 0;
## 3. 数据插入
ClickHouse 推荐批量插入,每次插入应尽量包含较多行(如 100,000 行),以减少写入开销。
sql
sql
-- 单行插入
INSERT INTO wikistat (time, project, subproject, path, hits)
VALUES ('2024-01-01 00:00:00', 'en', 'wikipedia', '/wiki/ClickHouse', 100);
-- 多行插入
INSERT INTO wikistat (time, project, subproject, path, hits)
VALUES
('2024-01-01 00:00:00', 'en', 'wikipedia', '/wiki/A', 10),
('2024-01-01 01:00:00', 'en', 'wikipedia', '/wiki/B', 20),
('2024-01-01 02:00:00', 'en', 'wikipedia', '/wiki/C', 30);
sql
-- 从 SELECT 插入
INSERT INTO target_table
SELECT * FROM source_table WHERE date >= '2024-01-01';
sql
-- 异步插入(适合少量频繁写入)
INSERT INTO YourTable
SETTINGS async_insert=1, wait_for_async_insert=1
VALUES (...);
4. SELECT 查询
sql
ClickHouse 的 SELECT 语法与标准 SQL 高度兼容,完整语法结构如下:
sql
SELECT [DISTINCT] expr_list
[FROM [db.]table | (subquery) | table_function]
[PREWHERE expr]
[WHERE expr]
[GROUP BY expr_list]
[HAVING expr]
[ORDER BY expr_list]
[LIMIT [n, ]m]
[SETTINGS ...]
基础查询
sql
sql
-- 查询全部列
SELECT * FROM wikistat LIMIT 10;
-- 只查询需要的列(推荐)
SELECT time, project, hits FROM wikistat LIMIT 100;
sql
-- 条件过滤
SELECT * FROM wikistat
WHERE project = 'en' AND hits > 1000
LIMIT 10;
时间分桶聚合
ClickHouse 内置丰富的时间函数,非常适合时序数据分析。
sql
sql
-- 按天聚合
SELECT toDate(time) AS date, sum(hits) AS hits
FROM wikistat
GROUP BY ALL
ORDER BY date ASC
LIMIT 5;
sql
sql
-- 按小时聚合
SELECT toStartOfHour(time) AS hour, sum(hits) AS hits
FROM wikistat
WHERE date(time) = '2015-07-01'
GROUP BY ALL
ORDER BY hour ASC
LIMIT 5;
sql
sql
-- 自定义时间间隔(4小时)
SELECT toStartOfInterval(time, INTERVAL 4 HOUR) AS interval, sum(hits) AS hits
FROM wikistat
WHERE date(time) = '2015-07-01'
GROUP BY ALL
ORDER BY interval ASC
LIMIT 6;
sql
动态列选择
sql
-- 使用正则匹配列名
SELECT COLUMNS('a') FROM col_names;
5. 聚合与分组
sql
sql
-- 基础聚合
SELECT
project,
count() AS cnt,
sum(hits) AS total_hits,
avg(hits) AS avg_hits,
max(hits) AS max_hits
FROM wikistat
GROUP BY project
ORDER BY total_hits DESC
LIMIT 10;
sql
-- 带 HAVING 过滤
SELECT subproject, sum(hits) AS total
FROM wikistat
WHERE project = 'it'
GROUP BY subproject
HAVING total > 1000000
ORDER BY total DESC;
6. JOIN 查询
sql
ClickHouse 支持标准 SQL JOIN,语法与 MySQL 类似。
sql
-- INNER JOIN
SELECT m.name AS movie_name, g.genre AS genre
FROM movies AS m
INNER JOIN genres AS g ON m.id = g.movie_id
ORDER BY m.year DESC
LIMIT 10;
sql
sql
-- LEFT JOIN
SELECT u.name, o.order_no
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
sql
-- CROSS JOIN
SELECT * FROM t1 CROSS JOIN t2;
7. 数据更新与删除
ClickHouse 的 DELETE 和 UPDATE 与 MySQL 有本质区别。DELETE 语句在 ClickHouse 中是轻量级删除,底层通过为受影响的行应用"掩码"来实现,删除的行会立即被后续查询过滤掉,数据清理在后台异步进行。
sql
sql
-- 轻量级删除(推荐)
DELETE FROM wikistat WHERE project = 'en' AND hits < 10;
sql
-- ALTER TABLE DELETE(Mutation 方式,会重写数据块)
ALTER TABLE wikistat DELETE WHERE hits < 10;
sql
-- 按分区删除(最高效)
ALTER TABLE wikistat DROP PARTITION '202401';
大规模删除会对 ClickHouse 性能产生负面影响。如果预计会频繁执行删除操作,建议考虑使用自定义分区键。
8. 表结构修改
sql
sql
-- 添加列
ALTER TABLE events ADD COLUMN favoriteNumber Float64 DEFAULT 7;
-- 修改列类型
ALTER TABLE events MODIFY COLUMN favoriteNumber Int64;
-- 重命名列
ALTER TABLE events RENAME COLUMN old_name TO new_name;
-- 删除列
ALTER TABLE events DROP COLUMN favoriteNumber;
-- 添加列并指定位置
ALTER TABLE events ADD COLUMN new_col String AFTER existing_col;
9. 物化视图
物化视图是 ClickHouse 中用于预聚合和加速查询的重要工具。它会在数据插入源表时自动计算并写入目标表,查询时直接查目标表即可。
sql
sql
-- 创建目标表
CREATE TABLE wikistat_top
(
`path` String,
`month` Date,
hits UInt64
)
ENGINE = SummingMergeTree
ORDER BY (month, hits);
-- 创建物化视图
CREATE MATERIALIZED VIEW wikistat_top_mv
TO wikistat_top
AS
SELECT
path,
toStartOfMonth(time) AS month,
sum(hits) AS hits
FROM wikistat
GROUP BY path, month;
使用物化视图后,原本需要 2.285 秒的查询可以降至毫秒级。
10. 常用函数
sql
sql
-- 日期时间
SELECT now(), today(), yesterday();
SELECT toDate('2024-01-15 10:30:00'); -- 转为日期
SELECT toStartOfHour(now()); -- 小时的开始
SELECT toStartOfDay(now()); -- 天的开始
SELECT toYYYYMM(now()); -- 年月数字
-- 字符串
SELECT length('hello');
SELECT substring('ClickHouse', 1, 5);
SELECT upper('hello'), lower('HELLO');
SELECT concat('a', 'b', 'c');
SELECT replace('hello world', 'world', 'ClickHouse');
-- 类型转换
SELECT toString(123);
SELECT toUInt32('456');
SELECT toFloat64('3.14');
-- 聚合
SELECT count(), uniq(project), uniqExact(project) FROM wikistat;
SELECT groupArray(project) FROM wikistat LIMIT 5;
SELECT quantile(0.5)(hits) FROM wikistat; -- 中位数
-- 条件
SELECT if(hits > 1000, 'high', 'low') FROM wikistat;
SELECT multiIf(hits > 10000, 'A', hits > 1000, 'B', 'C') FROM wikistat;
-- 空值处理
SELECT ifNull(nullable_col, 'default') FROM my_table;
SELECT coalesce(col1, col2, 'fallback') FROM my_table;
11. 查询性能优化
sql
核心原则
优化 ORDER BY 键是首要任务,因为它决定了主键索引,直接影响数据裁剪效率。
优化前后对比(查询 WHERE project = 'it'):
查询 ORDER BY (time) ORDER BY (project, subproject, time)
按 project 分组 2.381 秒 1.660 秒
按 subproject 分组 2.148 秒 0.058 秒
按月份分组 2.192 秒 0.012 秒
优化建议
不要使用 SELECT *,只查询需要的字段。ClickHouse 按需读取列,SELECT * 会读取所有列,增加不必要的 IO。
使用 PREWHERE 替代 WHERE,优先过滤数据,加速查询。
通过 LIMIT 限制返回数据量,节省计算资源和网络开销。
使用物化视图预聚合常用查询结果。
避免 Nullable 列,使用默认值代替,减少存储开销并提升查询性能。
sql
-- PREWHERE 示例:先过滤再读取其他列
SELECT project, sum(hits)
FROM wikistat
PREWHERE project = 'it'
WHERE hits > 100
GROUP BY project;
查看执行计划
sql
EXPLAIN SELECT * FROM wikistat WHERE project = 'en';
12. 分布式表
sql
在 ClickHouse 集群中,通常先创建本地表,再基于本地表创建分布式表。
sql
-- 1. 创建本地表(每个分片各有一份)
CREATE TABLE my_table_local ON CLUSTER my_cluster
(
id UInt64,
name String,
value Float64
)
ENGINE = MergeTree()
ORDER BY id;
-- 2. 创建分布式表(统一查询入口)
CREATE TABLE my_table_all ON CLUSTER my_cluster
AS my_table_local
ENGINE = Distributed(my_cluster, my_db, my_table_local, rand());
sql
-- 通过分布式表写入和查询
INSERT INTO my_table_all VALUES (1, 'a', 1.0), (2, 'b', 2.0);
SELECT * FROM my_table_all WHERE value > 1;
13. 总结
sql
本文整理了 ClickHouse 日常开发中的常用 SQL 语法,涵盖:
库表基础操作
MergeTree 建表与引擎选择
批量数据插入
SELECT 查询与时间分桶聚合
JOIN、聚合、分组
轻量级删除与表结构修改
物化视图加速查询
常用内置函数
查询性能优化核心原则
分布式表创建
与 MySQL 不同,ClickHouse 是列式数据库,更适合 OLAP 场景下的聚合分析查询。使用时应充分利用 ORDER BY 排序键、PARTITION BY 分区裁剪和物化视图来提升查询性能。