ClickHouse 语法优化规则

ClickHouse 的 SQL 优化规则是基于RBO(Rule Based Optimization),下面是一些优化规则

1 准备测试用表

1 上传官方的数据集

将visits_v1.tar和hits_v1.tar上传到虚拟机,解压到clickhouse数据路径下

// 解压到clickhouse数据路径

复制代码
sudo tar -xvf hits_v1.tar -C /var/lib/clickhouse`
`sudo tar -xvf visits_v1.tar -C /var/lib/clickhouse`

`//修改所属用户`
`sudo chown -R clickhouse:clickhouse /var/lib/clickhouse/data/datasets`
`sudo chown -R clickhouse:clickhouse /var/lib/clickhouse/metadata/datasets`
`

2 重启 clickhouse-server

sudo clickhouse restart

3 执行查询

clickhouse-client --query "SELECT COUNT(*) FROM datasets.hits_v1"

clickhouse-client --query "SELECT COUNT(*) FROM datasets.visits_v1"

注意:官方的tar包,包含了建库、建表语句、数据内容,这种方式不需要手动建库、建表,最方便。

hits _v1 表有1 30 多个字段,8 80 多万条数据

visits_ v1 表有1 80 多个字段,1 60 多万条数据

2 COUNT 优化

在调用 count 函数时,如果使用的是 count() 或者 count(*),且没有 where 条件,则会直接使用 system.tables 的 total_rows,例如:

复制代码
EXPLAIN SELECT count()FROM datasets.hits_v1;`

`Union`
`  Expression (Projection)`
`    Expression (Before ORDER BY and SELECT)`
`      MergingAggregated`
`        ReadNothing (Optimized trivial count)`
`

注意 Optimized trivial count ,这是对 count 的优化。

如果 count 具体的列字段,则不会使用此项优化:

复制代码
EXPLAIN SELECT count(CounterID) FROM datasets.hits_v1;`

`Union`
`  Expression (Projection)`
`    Expression (Before ORDER BY and SELECT)`
`      Aggregating`
`        Expression (Before GROUP BY)`
`          ReadFromStorage (Read from MergeTree)`
`

3 消除子查询重复字段

下面语句子查询中有两个重复的 id 字段,会被去重:

复制代码
EXPLAIN SYNTAX SELECT `
`   a.UserID,`
`   b.VisitID,`
`   a.URL,`
`   b.UserID`
`   FROM`
`   hits_v1 AS a `
`   LEFT JOIN (` 
`    SELECT       `
`      UserID,`        
`      UserID as HaHa,`       
`      VisitID   `
`    FROM visits_v1) AS b `
`   USING (UserID)`
`   limit 3;`

`//返回优化语句:`
`SELECT `
`    UserID,`
`    VisitID,`
`    URL,`
`    b.UserID`
`FROM hits_v1 AS a`
`ALL LEFT JOIN `
`(`
`    SELECT `
`        UserID,`
`        VisitID`
`    FROM visits_v1`
`) AS b USING (UserID)`
`LIMIT 3`
`

4 谓词下推

当group by有having子句,但是没有with cube、with rollup 或者with totals修饰的时候,having过滤会下推到where提前过滤。例如下面的查询,HAVING name变成了WHERE name,在group by之前过滤:

复制代码
EXPLAIN SYNTAX SELECT UserID FROM hits_v1 GROUP BY UserID HAVING UserID =` `'8585742290196126178';`

`//返回优化语句`
`SELECT UserID`
`FROM hits_v1`
`WHERE UserID = \'8585742290196126178\'`
`GROUP BY UserID`
`

子查询也支持谓词下推:

复制代码
EXPLAIN SYNTAX`
`SELECT *`
`FROM `
`(`
`    SELECT UserID`
`    FROM visits_v1`
`)`
`WHERE UserID =` `'8585742290196126178'`

`//返回优化后的语句`
`SELECT UserID`
`FROM `
`(`
`    SELECT UserID`
`    FROM visits_v1`
`    WHERE UserID = \'8585742290196126178\'`
`)`
`WHERE UserID = \'8585742290196126178\'

再来一个复杂例子:

复制代码
//返回优化后的语句`
`SELECT UserID`
`FROM `
`(`
`    SELECT UserID`
`    FROM `
    `(`
`        SELECT UserID`
`        FROM visits_v1`
`        WHERE UserID = \'8585742290196126178\'`
    `)`
`    WHERE UserID = \'8585742290196126178\'`
`    UNION ALL`
`    SELECT UserID`
`    FROM `
    `(`
`        SELECT UserID`
`        FROM visits_v1`
`        WHERE UserID = \'8585742290196126178\'`
    `)`
`    WHERE UserID = \'8585742290196126178\'`
`)`
`WHERE UserID = \'8585742290196126178\'`
`

5 聚合计算外推

聚合函数内的计算,会外推,例如:

复制代码
EXPLAIN SYNTAX`
`SELECT sum(UserID *` `2)`
`FROM visits_v1`

`//返回优化后的语句`
`SELECT sum(UserID)` `*` `2`
`FROM visits_v1`
`

6 聚合函数消除

如果对聚合键,也就是 group by key 使用 min、max、any 聚合函数,则将函数消除,例如:

复制代码
EXPLAIN SYNTAX`
`SELECT`
    `sum(UserID *` `2),`
    `max(VisitID),`
    `max(UserID)`
`FROM visits_v1`
`GROUP BY UserID`

`//返回优化后的语句`
`SELECT `
    `sum(UserID)` `*` `2,`
    `max(VisitID),`
`    UserID`
`FROM visits_v1`
`GROUP BY UserID`
`

7 删除重复的 order by key

例如下面的语句,重复的聚合键 id 字段会被去重:

复制代码
EXPLAIN SYNTAX`
`SELECT *`
`FROM visits_v1`
`ORDER BY`
`    UserID ASC,`
`    UserID ASC,`
`    VisitID ASC,`
`VisitID ASC`


`//返回优化后的语句:`
`select`
`	......`
`FROM visits_v1`
`ORDER BY `
`    UserID ASC,`
`VisitID ASC`
`

8 删除重复的 limit by key

例如下面的语句,重复声明的 name 字段会被去重:

复制代码
EXPLAIN SYNTAX`
`SELECT *`
`FROM visits_v1`
`LIMIT 3 BY`
`    VisitID,`
`    VisitID`
`LIMIT 10`


`//返回优化后的语句:`
`select`
`	......`
`FROM visits_v1`
`LIMIT 3 BY VisitID`
`LIMIT 10`
`

9 删除重复的 USING Key

例如下面的语句,重复的关联键 id 字段会被去重:

复制代码
EXPLAIN SYNTAX`
`SELECT`
`    a.UserID,`
`    a.UserID,`
`    b.VisitID,`
`    a.URL,`
`    b.UserID`
`FROM hits_v1 AS a`
`LEFT JOIN visits_v1 AS b USING (UserID, UserID)`


`//返回优化后的语句:`
`SELECT `
`    UserID,`
`    UserID,`
`    VisitID,`
`    URL,`
`    b.UserID`
`FROM hits_v1 AS a`
`ALL LEFT JOIN visits_v1 AS b USING (UserID)`
`

10 标量替换

如果子查询只返回一行数据,在被引用的时候用标量替换,例如下面语句中的 total_disk_usage 字段:

复制代码
EXPLAIN SYNTAX`
`WITH `
    `(`
`        SELECT sum(bytes)`
`        FROM system.parts`
`        WHERE active`
    `) AS total_disk_usage`
`SELECT`
    `(sum(bytes)` `/ total_disk_usage)` `*` `100 AS table_disk_usage,`
`    table`
`FROM system.parts`
`GROUP BY table`
`ORDER BY table_disk_usage DESC`
`LIMIT 10;`


`//返回优化后的语句:`
`WITH CAST(0, \'UInt64\') AS total_disk_usage`
`SELECT `
    `(sum(bytes)` `/ total_disk_usage)` `*` `100 AS table_disk_usage,`
`    table`
`FROM system.parts`
`GROUP BY table`
`ORDER BY table_disk_usage DESC`
`LIMIT 10`
`

11 三元运算优化

如果开启了 optimize_if_chain_to_multiif 参数,三元运算符会被替换成 multiIf 函数,例如:

复制代码
EXPLAIN SYNTAX `
`SELECT number =` `1` `?` `'hello'` `:` `(number =` `2` `?` `'world'` `:` `'atguigu')` 
`FROM numbers(10)` 
`settings optimize_if_chain_to_multiif =` `1;`

`// 返回优化后的语句:`
`SELECT multiIf(number =` `1, \'hello\', number =` `2, \'world\', \'atguigu\')`
`FROM numbers(10)`
`SETTINGS optimize_if_chain_to_multiif =` `1`
`
相关推荐
云和恩墨1 小时前
OceanBase企业版会话级SQL跟踪实操:DBMS_MONITOR(类Oracle 10046事件)
数据库·sql·oracle·oceanbase
为什么不问问神奇的海螺呢丶1 小时前
oracle 数据库巡检 sql
数据库·sql·oracle
未来之窗软件服务4 小时前
一体化系统(九)智慧社区综合报表——东方仙盟练气期
大数据·前端·仙盟创梦ide·东方仙盟·东方仙盟一体化
火星资讯8 小时前
Zenlayer AI Gateway 登陆 Dify 市场,轻装上阵搭建 AI Agent
大数据·人工智能
星海拾遗8 小时前
git rebase记录
大数据·git·elasticsearch
Elastic 中国社区官方博客8 小时前
Elasticsearch:在分析过程中对数字进行标准化
大数据·数据库·elasticsearch·搜索引擎·全文检索
聪明努力的积极向上8 小时前
【MYSQL】字符串拼接和参数化sql语句区别
数据库·sql·mysql
香精煎鱼香翅捞饭10 小时前
记一次多线程调用TDEngine restful获取数据的时间异常
大数据·时序数据库·tdengine
文刀竹肃11 小时前
DVWA -SQL Injection-通关教程-完结
前端·数据库·sql·安全·网络安全·oracle
Honmaple11 小时前
中国四级城市联动数据,包含港澳台,内含json , sql , python 脚本
python·sql·json