ClickHouse的WITH-ALIAS是如何实现的
ClickHouse的WITH-ALIAS是如何实现的
WITH-ALIAS包含相似但不同的两个特性:
- WITH <表达式> as <别名>
- WITH <别名> as <子查询>
WITH <表达式> as <别名> 特性
以下SQL展示了 WITH <表达式> as <别名> 特性的用法。
with c1 + 1 as c2 select c2 from t1;
运行的时候,select c2 中的别名c2会被改写为表达式c1+1。 使用With...Alias特性能够大大缩小SQL的大小。
WITH <表达式> as <别名> 的语法树
用EXPLAIN展现语法树。
explain ast with c1 as a1, f1() as a2 select a1, a2 from t1;
得到结果:
SelectWithUnionQuery (children 1)
ExpressionList (children 1)
SelectQuery (children 3)
ExpressionList (children 2)
Identifier c1 (alias a1)
Function f1 (alias a2) (children 1)
ExpressionList
ExpressionList (children 2)
Identifier a1
Identifier a2
TablesInSelectQuery (children 1)
TablesInSelectQueryElement (children 1)
TableExpression (children 1)
TableIdentifier t1
以上语法树关键之处是用Identifier,Function 内部包含alias别名属性,表示WITH子句中的"<表达式> as <别名>"。
所有继承于ASTWithAlias都带有alias别名属性。以下是继承树。
^ASTWithAlias$
└── ASTWithAlias
├── ASTFunction
├── ASTLiteral
├── ASTQueryParameter
├── ASTSubquery
└── ASTIdentifier
└── ASTTableIdentifier
ASTWithAlias 可以被指定为一个alias别名的语法单元,包括 ASTFunction, ASTLiteral, ASTQueryParameter, ASTSubquery, ASTIdentifier。举个例子,sum(abc) as sum_value 就是为ASTFunction 指明一个别名sum_value,column1 as c1也是另一个例子,为ASTIdentifier指定一个别名。
Enable_global_with_statement 开关
enable_global_with_statement开关会影响到表达式别名的处理。这个开关控制WITH...ALIAS是不是全局有效,“Propagate WITH statements to UNION queries and all subqueries”。如果关闭,ApplyWithAliasVisitor 和 ApplyWithGlobalVisitor 将不会工作,下面的SQL就会出错。
with 1 as a1
select a1
union all
select a1
union all
select a1
settings enable_global_with_statement =0;
settings enable_global_with_statement =0去掉就可以正常工作。
表达式别名的处理
表达式别名由ApplyWithAliasVisitor、ApplyWithGlobalVisitor、QueryAliasesVisitor、ExecuteScalarSubqueriesVisitor、QueryNormalizer访问者类来处理。
当打开enable_global_with_statement开关时,ApplyWithAliasVisitor和ApplyWithGlobalVisitor这两个访问者类会将表达式别名复制到subquery子查询和UNION语句中的其他SELECT查询(且称之为“兄弟查询”吧)中。
QueryAliasesVisitor访问者类会遍历整个AST树,搜集所有的表达式别名,构建出一个别名映射表,从String -> IAST。QueryNormalizer会根据收集的表达式别名把出现别名的地方替换成实际的AST(通过IAST的clone的新对象)。
ApplyWithAliasVisitor
遍历语法树,将外层的WITH的带别名的表达式(不包括子查询)复制到内层查询中。
with c1 + 1 as a1, c2 * 2 as a2
select * from (select a1, a2 from t1);
ApplyWithGlobalVisitor
将union select的第一个select的with子句传递给其他select语句。
以下是一个带with子句的union select的示例。
with 1 as a1
select a1
union all
select a1
union all
select a1
ApplyWithGlobalVisitor 会把第一个select上的WITH内容复制到其他select语句上。
QueryAliasesVisitor
收集AST树中的表达式别名,并且做以下两件事情:
- 对于没有alias别名的subquery子查询,此Visitor会为其加上唯一的alias别名;
- 保证其上的prefer_alias_to_column_name属性置为true。
为subquery子查询加上别名是因为subquery会被随后的优化器修改,导致以subquery内容生成subquery字段名的算法无法使用,因为当subquery的内容被优化器改变后,此算法生成的字段名就会更以前的不一样,这样会破坏一个不变式:同一个subquery在不同的时候对应的字段名必须一样。
Subquery子查询在随后的处理中可能会变成字面值。例如把子查询(SELECT sum(number) FROM numbers(10)) 变为 (SELECT sum(number) FROM numbers(10)) as _subquery_1。这个子查询会被ExecuteScalarSubqueriesVisitor转换成最终的字面值,即(45 as _subquery_1)。
需要显式地将prefer_alias_to_column_name 设置为true的原因是关于分布式表引擎Distributed engine的一个缺陷。对于分布式表引擎Distributed engine,查询被发到远程服务器时会丢失prefer_alias_to_column_name属性,因此对于带_subquery_前缀的别名的子查询或者是字面值,QueryAliasesVisitor会将其prefer_alias_to_column_name属性重新置为true。
对于ARRAY JOIN和LEFT ARRAY JOIN有特殊处理逻辑,跳过其AST树上的一级和二级子节点,专处理第三级子节点,这里不是很优雅,可能会隐藏着bug。
ExecuteScalarSubqueriesVisitor
将可以求值为一个单值的子查询替换成求出的单值,作为字面值替代原有的子查询。例如子查询(SELECT sum(number) FROM numbers(10)) as _subquery_1会变成(45 as _subquery_1)。其上的prefer_alias_to_column_name属性会被置为true。
QueryNormalizer
根据别名映射表,替换别名为实体AST。有一些设置开关控制其行为细节:
max_expanded_ast_elements
展开后的AST节点的数量上限。
max_ast_depth
AST树的深度限制。prefer_column_name_to_alias
尽量用列名替代别名。allow_self_aliases
允许a1 + 1 as a1这样的表达式别名,现在的代码中allow_self_aliases都为true。
WITH <别名> as <子查询> 特性
WITH...SUBQUERY是另外一个相似的特性,用于定义WITH上的子查询,在SQL的需要表的地方(例如FROM后面)可以引用。下面是一个例子:
with q1 as (select * from t1), q2 as (select * from t2) select * from q1;
WITH <别名> as <子查询> 与 WITH <表达式> as <别名>虽然很类似但是生成的语法树差别很大。
WITH <别名> as <子查询> 的语法树
用EXPLAIN展现语法树。
explain ast with q1 as (select * from t1), q2 as (select * from t2) select * from q1;
得到结果:
SelectWithUnionQuery (children 1)
ExpressionList (children 1)
SelectQuery (children 3)
ExpressionList (children 2)
WithElement (children 1)
Subquery (children 1)
SelectWithUnionQuery (children 1)
ExpressionList (children 1)
SelectQuery (children 2)
ExpressionList (children 1)
Asterisk
TablesInSelectQuery (children 1)
TablesInSelectQueryElement (children 1)
TableExpression (children 1)
TableIdentifier t1
WithElement (children 1)
Subquery (children 1)
SelectWithUnionQuery (children 1)
ExpressionList (children 1)
SelectQuery (children 2)
ExpressionList (children 1)
Asterisk
TablesInSelectQuery (children 1)
TablesInSelectQueryElement (children 1)
TableExpression (children 1)
TableIdentifier t2
ExpressionList (children 1)
Asterisk
TablesInSelectQuery (children 1)
TablesInSelectQueryElement (children 1)
TableExpression (children 1)
TableIdentifier q1
以上语法树关键之处是用WithElement 表示WITH子句中的"<别名> as <子查询>"。
如果一个查询为子查询,它是用ASTSubquery来表示,否则就是用ASTSelectQuery来表示。在ASTSelectQuery中包含WITH部分,通过with()方法获得。
子查询别名的处理
子查询别名的处理相对比较简单。访问者类ApplyWithSubqueryVisitor遍历AST树,替换别名为真实子查询AST实例。
ApplyWithSubqueryVisitor
遍历语法树,将WITH中的带别名的子查询替换引用子查询别名的地方。
其他细节
SELECT的语法树结构
即使只有一个select,也会被套进 SelectWithUnionQuery里面,形成以下三层结构。
SelectWithUnionQuery (children 1)
ExpressionList (children 1)
SelectQuery (children 2)
用一个最简单的为例,
explain ast select 1;
生成的语法树,
SelectWithUnionQuery (children 1)
ExpressionList (children 1)
SelectQuery (children 1)
ExpressionList (children 1)
Literal UInt64_1
prefer_alias_to_column_name的作用
prefer_alias_to_column_name用于指示用别名作为内部的列的名字,这个可以避免为了得到内部列名而需要的大量的递归调用IAST::appendColumnName方法。
ASTArrayJoin
ARRAY JOIN和LEFT ARRAY JOIN是一种特殊的语法,在代码中经常被特殊处理。以下是它的AST树的示例:
CREATE TABLE arrays_test
(
s String,
arr Array(UInt8)
) ENGINE = Memory;
INSERT INTO arrays_test
VALUES ('Hello', [1,2]), ('World', [3,4,5]), ('Goodbye', []);
SELECT s, arr
FROM arrays_test
ARRAY JOIN arr;
以上SQL中的SELECT查询的AST为:
SelectWithUnionQuery (children 1)
ExpressionList (children 1)
SelectQuery (children 2)
ExpressionList (children 2)
Identifier s
Identifier arr
TablesInSelectQuery (children 2)
TablesInSelectQueryElement (children 1)
TableExpression (children 1)
TableIdentifier arrays_test
TablesInSelectQueryElement (children 1)
ArrayJoin (children 1)
ExpressionList (children 1)
Identifier arr
总结
表达式别名比子查询别名在处理上要复杂的多,因为表达式本身的处理就很复杂。
别名机制可以减少SQL查询的大小,但是展开后的AST树的大小不会缩小。虽然如此,却可以在访问者模式的某些Visitor的处理过程中减少Visitor访问的工作量。
ClickHouse的WITH-ALIAS是如何实现的的更多相关文章
- Clickhouse副本表以及分布式表简单实践
集群配置: 192.168.0.106 node3 192.168.0.101 node2 192.168.0.103 node1 zookeeper配置忽略,自行实践! node1配置: <? ...
- ClickHouse
ClickHouse 是俄罗斯的Yandex于2016年开源的列式存储数据库(DBMS),主要用于在线分析处理查询(OLAP),能够使用SQL查询实时生成分析数据报告 1 安装前的准备1.1 Cent ...
- ClickHouse学习笔记
1. 概述 ClickHouse是一个用于联机分析(OLAP:Online Analytical Processing)的列式数据库管理系统(DBMS:Database Management Syst ...
- 【大数据】Clickhouse基础知识
第1章 ClickHouse概述 1.1 什么是ClickHouse ClickHouse 是俄罗斯的Yandex于2016年开源的列式存储数据库(DBMS),主要用于在线分析处理查询(OLAP),能 ...
- clickhouse入门到实战及面试
第一章. clickhouse入门 一.ClickHouse介绍 ClickHouse(开源)是一个面向列的数据库管理系统(DBMS),用于在线分析处理查询(OLAP). 关键词:开源.面向列.联机分 ...
- Clickhouse 入门
clickhouse 简介 ck是一个列式存储的数据库,其针对的场景是OLAP.OLAP的特点是: 数据不经常写,即便写也是批量写.不像OLTP是一条一条写 大多数是读请求 查询并发较少,不适合放置先 ...
- 如何在Bash脚本中引入alias
更多精彩内容,请关注微信公众号:后端技术小屋 alias的使用 在日常开发中,为了提高运维效率,我们会用alias(命令别名)来定义命令的简称.比如在~/.bash_profile中添加: alias ...
- 初识ClickHouse——安装与入门
前言: 久闻 ClickHouse 大名,一直没有去详细了解.近期看了下 ClickHouse 相关文档,决定安装体验下.想了解 ClickHouse 的小伙伴可以一起跟着学习哦.本篇文章主要介绍 C ...
- ClickHouse学习系列之五【系统库system说明】
背景 之前介绍过ClickHouse相关的系列文章,现在ClickHouse已经能正常使用起来了,包括副本和分片.因为ClickHouse已经可以提供服务了,现在需要关心的就是服务期间该数据库的各项性 ...
- ClickHouse学习系列之七【系统命令介绍】
背景 前面介绍了ClickHouse相关的系列文章,该系列文章包括了安装.权限管理.副本分片.配置说明等.这次介绍一些ClickHouse相关的系统命令,如重载配置文件.关闭服务和进程.停止和启动后 ...
随机推荐
- 数字孪生为何开始逐渐与GIS进行融合?
近年来,数字孪生技术和地理信息系统(GIS)在各自领域的快速发展引起了广泛关注.这两个技术的结合被认为是一种强大的联合,可以为各行各业带来革命性的变革和创新.那么,为何数字孪生开始逐渐与GIS进行融合 ...
- 轻量对象存储 LighthouseCOS 用户实践征文
产品使用攻略.上云技术实践,有奖征集,多重好礼等您带回家- 存储桶一键挂载轻量应用服务器,简单易用,腾讯云轻量对象存储用户实践征文活动特惠:腾讯云轻量云专场特惠活动. 投稿说明 注册/登录腾讯云账号, ...
- P2343 宝石管理系统 做题记录
随机跳的. 一眼带修第 \(\text{k}\) 大,平衡树 / 权值线段树 / set 随便搞就行. (set 可能要双 \(\log\),所以没写) 很快啊,权值线段树就 \(\text{A}\) ...
- 一行代码解决Three.js中只能在一侧看到物体的问题
项目场景: 因为该项目比较复杂庞大,在此就简单介绍一下: 通过Three.js创建若干个物体进行了组装,从而形成了一个类似眼球模拟模型的项目,用户可以通过拖动鼠标来达到控制视角(摄像机)的目的 ...
- SPL:跑批有这么难么?
摘要:SPL实现了更优算法,性能远远超过存储过程,能显著提高单机计算效率,非常适合跑批计算. 本文分享自华为云社区<Java开源专业计算引擎:跑批真的这么难吗?>,作者: Java李杨勇. ...
- 华为云数据治理生产线DataArts,让“数据‘慧’说话”
摘要:数据治理生产线DataArts改变了传统"人拉肩抗"的数据处理方式,帮助提升效率:降低技术门槛,让"人人都是分析师":让"数据'慧'说话&quo ...
- 鲲鹏BoostKit虚拟化使能套件,让数据加密更安全
摘要:借助华为鲲鹏BoostKit虚拟化使能套件(简称鲲鹏BoostKit虚拟化),可加速迈向云计算之旅.本次KAE加速引擎让数据加密更安全直播将介绍鲲鹏BoostKit加速库全景,基于BoostKi ...
- SEAL 0.3 正式发布:国内首个全链路软件供应链安全管理平台
12月1日,软件供应链安全管理平台 SEAL 0.3 正式发布(以下简称"SEAL"),这是国内首个以全链路视角保护软件供应链的安全管理平台.两个月前 SEAL 0.2 发布,该版 ...
- 如何实现数据流畅转换?火山引擎ByteHouse推出ELT能力
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 在数据分析场景中,企业使用的数据通常具备来源多样化的特点,如支付交易记录.用户行为等,且数据格式各异,有的为行 ...
- 查询速度最高提升50倍!火山引擎ByteHouse在广告投放领域实践分享
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 据QuestMobile报告显示,移动互联网已经进入了下半场,在使用人数和使用时长方面已经没有明显增长,互联 ...