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)ExpressionListExpressionList (children 2)Identifier a1Identifier a2TablesInSelectQuery (children 1)TablesInSelectQueryElement (children 1)TableExpression (children 1)TableIdentifier t1

以上语法树关键之处是用IdentifierFunction 内部包含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_valuecolumn1 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树中的表达式别名,并且做以下两件事情:

  1. 对于没有alias别名的subquery子查询,此Visitor会为其加上唯一的alias别名;
  2. 保证其上的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 JOINLEFT 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。有一些设置开关控制其行为细节:

  1. max_expanded_ast_elements

    展开后的AST节点的数量上限。

  2. max_ast_depth
    AST树的深度限制。

  3. prefer_column_name_to_alias
    尽量用列名替代别名。

  4. 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)AsteriskTablesInSelectQuery (children 1)TablesInSelectQueryElement (children 1)TableExpression (children 1)TableIdentifier t1WithElement (children 1)Subquery (children 1)SelectWithUnionQuery (children 1)ExpressionList (children 1)SelectQuery (children 2)ExpressionList (children 1)AsteriskTablesInSelectQuery (children 1)TablesInSelectQueryElement (children 1)TableExpression (children 1)TableIdentifier t2ExpressionList (children 1)AsteriskTablesInSelectQuery (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 JOINLEFT 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 sIdentifier arrTablesInSelectQuery (children 2)TablesInSelectQueryElement (children 1)TableExpression (children 1)TableIdentifier arrays_testTablesInSelectQueryElement (children 1)ArrayJoin (children 1)ExpressionList (children 1)Identifier arr

总结

表达式别名比子查询别名在处理上要复杂的多,因为表达式本身的处理就很复杂。

别名机制可以减少SQL查询的大小,但是展开后的AST树的大小不会缩小。虽然如此,却可以在访问者模式的某些Visitor的处理过程中减少Visitor访问的工作量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/75186.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FPGA通信—千兆网(UDP)软件设计

一、PHY引脚功能描述 引脚功能描述1CLK25 CLK125:内部PLL生成的125MHz参考时钟&#xff0c;如MAC未使用125MHe时钟&#xff0c;则此引脚应保持浮动&#xff0c; 2 4 63 GND 接地3REG OUT开关压器&#xff0c;1.05V输出 5 6 8 9 11 12 14 15 MDI[0] MDI[0]- MDI[1] MDI[1…

学习笔记-BNF、EBNF、ABNF语法格式描述规范

目标是确认一些c/cpp的语法细节&#xff0c;需要看cpp语法定义文件。 考虑从c的语法定义文件开始确认。 考虑实现一个简化的语言定义和编译器&#xff0c;为后续的实际需求做自定义扩展。 参考网页&#xff1a; https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_f…

高可用Kuberbetes部署Prometheus + Grafana

概述 阅读官方文档部署部署Prometheus Grafana GitHub - prometheus-operator/kube-prometheus at release-0.10 环境 步骤 下周官方github仓库 git clone https://github.com/prometheus-operator/kube-prometheus.git git checkout release-0.10 进入工作目录 cd kube…

【linux命令讲解大全】100. Linux常用文件传输命令详解

文章目录 cd概要主要用途参数选项返回值例子关于切换到上一个工作目录的说明关于 CDPATH关于 cdable_vars注意 从零学 python cd 切换用户当前工作目录。 概要 cd [-L|[-P [-e]]] [dir] 主要用途 切换工作目录至 dir。其中 dir 的表示法可以是绝对路径或相对路径。若参数 …

二、[mysql]之Explain讲解与实战

目录 一、了解Explain1.Explain介绍 二、Explain相关字段1.partitions2.filtered3.SHOW WARNINGS命令 三、Explain比较重要字段1.id2.select_type3.table4.type5.possible_keys6.key7.key_len8.ref9.rows10.Extra 四、索引优化实战&#xff08;遵循原则&#xff09;1.全值匹配2…

Linux 系统中查看和停止删除定时任务

文章目录 linux系统定时任务之crontab什么是crontabcrond 在 Linux 系统中查看当前配置的定时任务停止和删除定时任务的方法cron定时任务控制防火墙恢复 linux系统定时任务之crontab 什么是crontab 在Linux或类Unix系统中&#xff0c;通常使用crontab命令在指定的时间执行一个…

python关闭指定进程以excel为例

先说下环境&#xff1a; Excel版本&#xff1a; Python2.7.13和Python3.10.4并存。 2、打开两个excel工作簿 看进程是这样的&#xff1a; 3、用python编程kill进程 # -*- coding: utf-8 -*- import os proc_nameEXCEL.EXE if __name__ __main__:os.system(taskkill /im {} /…

【vue2第十六章】VueRouter 声明式导航(跳转传参)、路由重定向、页面未找到的提示页面404、vue路由模式设置

声明式导航(跳转传参) 在一些特定的需求中&#xff0c;跳转路径时我们是需要携带参数跳转的&#xff0c;比如有一个搜索框&#xff0c;点击搜索的按钮需要跳转到另外一个页面组件&#xff0c;此时需要把用户输入的input框的值也携带到那页面进行发送请求&#xff0c;请求数据。…

python 随机生成emoji表情

问答板块觉得比较有意思的问题 当时搜了些网上的发现基本都不能用&#xff0c;不知道是版本的问题还是咋的就开始自己研究 python随机生成emoji 问题的产生解决官网文档数据类型实现思路实现前提&#xff1a;具体实现&#xff1a; 其他常见用法插入 Emoji 表情&#xff1a;解析…

【ES6】Class中this指向

先上代码&#xff1a; 正常运行的代码&#xff1a; class Logger{printName(name kexuexiong){this.print(hello ${name});}print(text){console.log(text);} }const logger new Logger(); logger.printName("kexueixong xiong");输出&#xff1a; 单独调用函数p…

【Java基础】学习笔记5 - 包装类与集合介绍

目录 第四阶段包装类StringStringBufferArraysSystemCollectionArrayListVector 第四阶段 包装类 装箱拆箱以及手动自动 package chapter4;public class Pack {public static void main(String[] args) {// 自动装箱int a 100;Integer aa a;// 自动拆箱int b aa;// 手动装…

搭建自己的OCR服务,第二步:PaddleOCR环境安装

PaddleOCR环境安装&#xff0c;遇到了很多问题&#xff0c;根据系统不同问题也不同&#xff0c;不要盲目看别人的教程&#xff0c;有的教程也过时了&#xff0c;根据实际情况自己调整。 我这边目前是使用windows 10系统CPU python 3.7 搭建。 熟悉OCR的人应该知道&#xff0…

合宙Air724UG LuatOS-Air LVGL API控件-标签 (Label)

标签 (Label) 标签是 LVGL 用来显示文字的控件。 示例代码 label lvgl.label_create(lvgl.scr_act(), nil) lvgl.label_set_recolor(label, true) lvgl.label_set_text(label, "#0000ff Re-color# #ff00ff words# #ff0000 of\n# align the lines …

时间管理类书籍阅读笔记

背景 这段时间看了时间管理方面的书籍&#xff0c;大部分和早晨时间利用相关。之所以有了利用早晨时间的想法&#xff0c;是某天下班后&#xff0c;感觉很疲惫&#xff0c;什么都不想做&#xff0c;于是就打了一晚上游戏&#xff0c;然后第二天重复着这样的生活。 突然意识到…

基于Spring、SpringMVC、Mybatis的超市管理系统

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于SSM的超市订单管理系统,java项目。 …

微信小程序替换双重循环渲染中报错图片

当微信小程序中的图片加载失败导致报错时&#xff0c;你可以通过以下步骤替换报错图片&#xff1a; 确保你有一个用于替换报错图片的备用图片&#xff0c;可以是与原始图片相同尺寸和格式的另一张图片。 在相关的.wxml文件中&#xff0c;找到显示报错图片的 <image> 标签…

AWK语言第二版 2.2选择

2.2 选择 Awk的基本结构组成&#xff0c;就是用一系列的样式选择出感兴趣的行&#xff0c;并对其进行操作。这样的程序很多都是一次性的&#xff0c;敲出来之后就用几次。然而&#xff0c;有些程序很有用&#xff0c;如果每次用之前都要重新敲一遍代码&#xff0c;就太麻烦了&…

Linux修改fs.inotify.max_user_watches(“外部文件更改同步可能很慢”和“当前的 inotify(7) 监视限制太低”)

fs.inotify.max_user_watches 参数是用于控制 Linux 内核中 inotify 子系统的观察者数量限制。inotify 是一种文件系统监控机制&#xff0c;它可以用于检测文件或目录的变化&#xff0c;并在事件发生时通知相关的应用程序。 具体而言&#xff0c;fs.inotify.max_user_watches …

B站:AB test [下]

Focus在&#xff1a;AB Test结束后&#xff0c;如何进行显著性检验&#xff1f;&#xff08;以判断改动是否有效果&#xff09; 引入&#xff1a;Z检验和T检验 而T检验适用于 n<30 的小样本 值得注意的是&#xff1a;统计上显著并不意味着现实中显著&#xff01; e.g. 加速…

尚硅谷大数据项目《在线教育之离线数仓》笔记008

视频地址&#xff1a;尚硅谷大数据项目《在线教育之离线数仓》_哔哩哔哩_bilibili 目录 P123 P124 P125 P126 P127 P128 P129 P123 Apache Superset是一个现代的数据探索和可视化平台。它功能强大且十分易用&#xff0c;可对接各种数据源&#xff0c;包括很多现代的大数…