为什么 Recursive CTE 不支持 Union Distinct ?

【 使用环境 】环境:OBCloud
【 使用版本 】版本 :5.7.25-OceanBase-v4.3.5.1
【问题描述】Recursive CTE 不支持 Union Distinct


【复现路径】 使用 Recursive CTE 时使用 Union Distinct
【我的问题】

  1. 为什么 Recursive CTE 不支持 Union Distinct ,有什么技术考虑?
  2. 不支持 Distinct 的情况下,Oceanbase 的 Recursive CTE 如何做环路检测打破存在环路的无限递归?@论坛小助手

根据文档库中的信息,以下是关于 Recursive CTE 不支持 UNION DISTINCT 的详细解答:

1. 为什么 Recursive CTE 不支持 UNION DISTINCT,有什么技术考虑?

依据 文档片段 4 中的描述,OceanBase 数据库明确指出递归公共表达式(CTE)应该包含一个 UNION ALL 操作。具体错误信息为:

ERROR 3573 (HY000): Recursive Common Table Expression should contain a UNION ALL.

从技术角度来看,UNION DISTINCT 在递归 CTE 中存在以下问题:

  • 性能开销UNION DISTINCT 需要对结果集进行去重操作,这在递归查询中可能导致较大的性能开销,尤其是在数据量较大或递归深度较深的情况下。
  • 实现复杂性:递归查询通常涉及多次迭代,每次迭代的结果需要与之前的结果合并。如果使用 UNION DISTINCT,则需要在每次迭代时对累积结果进行去重,增加了实现的复杂性。
  • 无限递归的风险:递归查询中可能存在环路(即某个节点通过路径回到自身),UNION DISTINCT 虽然可以部分解决环路问题,但其代价是显著降低性能,并且可能无法完全避免无限递归。

因此,OceanBase 数据库选择仅支持 UNION ALL,以简化递归 CTE 的实现并提高性能。


2. 不支持 DISTINCT 的情况下,OceanBase 的 Recursive CTE 如何做环路检测打破存在环路的无限递归?

在不支持 DISTINCT 的情况下,可以通过以下方法来检测和打破环路,防止无限递归:

方法一:引入额外列记录路径

可以在递归查询中引入一个额外的列(如路径字符串或数组),用于记录当前节点的访问路径。如果发现路径中已经包含当前节点,则停止递归。

示例 SQL:

WITH RECURSIVE subordinates AS (
    SELECT id, name, manager_id, department_id, salary, CAST(id AS CHAR(100)) AS path
    FROM employees
    WHERE id < 10
    UNION ALL
    SELECT e.id, e.name, e.manager_id, e.department_id, e.salary, CONCAT(s.path, ',', e.id)
    FROM employees e
    INNER JOIN subordinates s ON e.manager_id = s.id
    -- 环路检测:确保路径中不包含当前节点
    WHERE FIND_IN_SET(e.id, s.path) = 0
)
SELECT * FROM subordinates;

在这个例子中,path 列记录了每个节点的访问路径,FIND_IN_SET 函数用于检测路径中是否已经包含当前节点。如果路径中已包含当前节点,则跳过该节点,从而避免无限递归。

方法二:限制递归深度

可以通过设置递归的最大深度来防止无限递归。OceanBase 数据库默认会对递归深度进行限制,用户也可以通过配置参数调整递归深度。

示例 SQL:

WITH RECURSIVE subordinates AS (
    SELECT id, name, manager_id, department_id, salary, 1 AS level
    FROM employees
    WHERE id < 10
    UNION ALL
    SELECT e.id, e.name, e.manager_id, e.department_id, e.salary, s.level + 1
    FROM employees e
    INNER JOIN subordinates s ON e.manager_id = s.id
    -- 限制递归深度
    WHERE s.level < 10
)
SELECT * FROM subordinates;

在这个例子中,level 列用于记录递归的深度,当递归深度超过指定值时,停止递归。


总结

  1. 为什么不支持 UNION DISTINCT:递归 CTE 使用 UNION DISTINCT 会导致性能开销过大、实现复杂性增加以及潜在的无限递归风险。因此,OceanBase 数据库仅支持 UNION ALL
  2. 如何打破环路:可以通过引入额外列记录路径或限制递归深度的方式来检测和打破环路,防止无限递归。

如果您有更多疑问,欢迎进一步提问!

检索到的文档如下:

  1. obcdc 支持增量旁路导入
  2. OceanBase 数据库概述
  3. OceanBase 概述
  4. 0001 ~ 3999
  5. 全链路追踪概述
  6. 产品 FAQ
  7. 向量检索概述
  8. 在 OceanBase 数据库上进行 TPC-C 测试

当前版本确实没支持这个,需要435 BP4,9月初带上