WITH 子句
ClickHouse 支持公共表表达式(CTE),并将 WITH
子句中定义的代码在 SELECT
查询的所有使用位置进行替换。命名子查询可以在允许表对象的地方被包含到当前和子查询上下文中。通过隐藏当前级别的 CTE 来防止递归。
请注意,CTE 在被调用的所有地方并不能保证返回相同的结果,因为查询会为每个用例重新执行。
以下是这样的行为的示例:
如果 CTE 传递的是确切的结果而不仅仅是一段代码,你总会看到 1000000
。
然而,由于我们两次引用了 cte_numbers
,每次都会生成随机数字,因此我们看到不同的随机结果,如 280501, 392454, 261636, 196227
等等...
语法
或
示例
示例 1: 使用常量表达式作为“变量”
示例 2: 从 SELECT 子句列列表中排除 sum(bytes) 表达式结果
示例 3: 使用标量子查询的结果
示例 4: 在子查询中重用表达式
递归查询
可选的 RECURSIVE 修饰符允许 WITH 查询引用其自身的输出。示例:
示例: 求 1 到 100 的整数和
递归 CTE 依赖于在版本 24.3
中引入的 新查询分析器。如果你使用的是版本 24.3+
并遇到 (UNKNOWN_TABLE)
或 (UNSUPPORTED_METHOD)
异常,这表明新分析器在你的实例、角色或配置中已被禁用。要启用分析器,请开启 allow_experimental_analyzer
设置或将 compatibility
设置更新为更高版本。
从版本 24.8
开始,新分析器已完全推广到生产环境,设置 allow_experimental_analyzer
已更名为 enable_analyzer
。
递归 WITH
查询的一般形式始终是一个非递归项,然后是 UNION ALL
,接着是一个递归项,其中只有递归项可以包含对查询自身输出的引用。递归 CTE 查询执行的步骤如下:
- 评估非递归项。将非递归项查询的结果放入临时工作表中。
- 只要工作表不为空,重复以下步骤:
- 评估递归项,替换工作表当前内容为递归自引用。将递归项查询的结果放入临时中间表中。
- 用中间表的内容替换工作表的内容,然后清空中间表。
递归查询通常用于处理层次结构或树状结构数据。例如,我们可以编写一个执行树遍历的查询:
示例: 树遍历
首先,我们创建树表:
我们可以用这样的查询遍历这些树:
示例: 树遍历
搜索顺序
为了创建深度优先顺序,我们为每个结果行计算一个已访问行的数组:
示例: 树遍历深度优先顺序
为了创建宽度优先顺序,标准的方法是添加一个跟踪搜索深度的列:
示例: 树遍历宽度优先顺序
循环检测
首先,创建图表:
我们可以用这样的查询遍历图表:
示例: 无循环检测的图表遍历
但如果我们在图表中添加循环,之前的查询将失败并出现 Maximum recursive CTE evaluation depth
错误:
处理循环的标准方法是计算一个已经访问的节点数组:
示例: 带有循环检测的图形遍历
无限查询
如果在外部查询中使用 LIMIT
,也可以使用无限递归的 CTE 查询:
示例: 无限递归 CTE 查询