公共表表达式
公共表表达式表示命名子查询。 在SELECT 查询中,凡是允许使用表表达式的地方,都可以通过名称引用它们。
命名子查询可以在当前查询的作用域内,或在子查询的作用域内通过名称引用。
在 SELECT 查询中,每次引用公共表表达式时,如果该 CTE 未被显式定义为物化 (参见物化公共表表达式) ,都会被替换为其定义中的子查询。
通过在标识符解析过程中隐藏当前 CTE,可以防止递归。
请注意,CTE 并不保证在所有被调用的位置都产生相同的结果,因为每次使用时都会重新执行该查询。
语法
示例
以下是子查询被重新执行的一个示例:1000000
然而,由于我们引用了两次 cte_numbers,每次都会生成随机数,因此会看到不同的随机结果,例如 280501, 392454, 261636, 196227 等等…
物化公共表表达式
默认情况下,ClickHouse 会在每处引用时内联 CTE 的子查询,因此每次引用都会重新执行该子查询。 添加MATERIALIZED 关键字后,ClickHouse 会将 CTE 子查询只执行一次,并将结果存储在临时表中,后续所有引用都从该表读取。
这在同一个 CTE 在一次查询中被多次引用时尤其有用 (例如自连接或多个 IN 子查询) ,因为底层计算只需执行一次。
物化 CTE 是一项 Experimental 功能。
它要求启用设置
enable_materialized_cte。
如果该设置被禁用,MATERIALIZED 关键字将被忽略:CTE 会像普通 CTE 一样在每处引用时被内联,并记录一条警告日志。语法
何时使用
在以下情况下,物化 CTE 最有用:- 同一个 CTE 在一次查询中被引用不止一次。
如果没有
MATERIALIZED,每次引用都会独立重新执行该子查询。 - 该 CTE 包含像
generateRandom这样的非确定性函数。 物化后可确保所有引用看到的是同一份数据。 - 该 CTE 涉及开销较大的计算 (聚合、连接、大规模扫描) ,不应重复执行。
示例
示例 1: 对物化 CTE 执行自连接 如果不使用MATERIALIZED,JOIN 的两侧都会各自执行该子查询。
使用 MATERIALIZED 后,该表只会被扫描一次,JOIN 的两侧都会从同一个临时表中读取。
generateRandom 的常规 CTE 在每次引用时都会产生不同的结果。
将 CTE 物化后可确保结果一致:
1000000。
示例 3: 串联物化 CTE
物化 CTE 可以引用其他物化 CTE。
ClickHouse 会解析这些依赖关系,并按正确顺序将其物化:
限制
- 需要启用 Experimental 设置:必须启用设置
enable_materialized_cte。如果该设置被禁用,MATERIALIZED关键字将被忽略:该 CTE 会像普通 CTE 一样在每个引用处内联,并记录一条警告日志。 - 不支持与
RECURSIVE一起使用:不允许同时使用MATERIALIZED和RECURSIVE关键字,否则会引发UNSUPPORTED_METHOD异常。 - 禁止使用关联 CTE:物化 CTE 不能引用外层查询作用域中的列。
通用标量表达式
ClickHouse 允许你在WITH 子句中将任意标量 expression 声明为别名。
通用标量表达式可以在查询中的任何位置引用。
语法
示例
示例 1: 将常量表达式用作”变量”extension 在 gen_name lambda 函数的函数体中未绑定。
尽管 extension 在 generated_names 的定义和使用所在的作用域中被定义为通用标量表达式 '.txt',但它会被解析为表 extension_list 中的一列,因为它在 generated_names 子查询中可用。
递归查询
可选的RECURSIVE 修饰符允许 WITH 查询引用其自身的输出。示例:
**示例:**对 1 到 100 的整数求和
递归 CTE 依赖于在版本
24.3 中引入的查询分析器,自该版本起默认启用,并从 26.9 起强制启用。在实例、角色或 profile 中仍禁用 analyzer 的旧版本上,递归 CTE 会引发 (UNKNOWN_TABLE) 或 (UNSUPPORTED_METHOD) 异常;请在相应位置启用 enable_analyzer 设置,或进行升级。WITH 查询的一般形式始终为:先是一个非递归项,然后是 UNION ALL,最后是一个递归项,其中只有递归项可以包含对查询自身输出的引用。递归 CTE 查询的执行方式如下:
- 计算非递归项。将非递归项查询的结果放入临时工作表中。
- 只要工作表不为空,就重复以下步骤:
- 计算递归项,将工作表的当前内容替换递归自引用。将递归项查询的结果放入临时中间表中。
- 用中间表的内容替换工作表中的内容,然后清空中间表。
搜索顺序
为了生成深度优先顺序,我们为结果中的每一行计算一个由已访问行构成的数组: 示例: 树遍历的深度优先顺序循环检测
首先创建 graph 表:Maximum recursive CTE evaluation depth 错误而失败:
无限查询
只要在外层查询中使用LIMIT,也可以使用无限递归 CTE 查询:
示例: 无限递归 CTE 查询
行尾逗号
WITH 子句中的最后一个元素后可以加逗号: