从v2开始,Oracle SQL可以使用其专有的CONNECT BY语法进行分层查询。在最新的11g版本2中,他们添加了递归子查询因子,也称为递归with子句。这是ANSI标准,如果我理解正确,那么其他RDBMS供应商也已经实现了这一标准。
在比较connect- by和递归方式时,我发现使用周期检测时结果集有所不同。按结果进行连接对我来说更直观,因此我想知道Oracle的实现中是否包含错误,或者这是否是标准ANSI和预期的行为。因此,我的问题是,是否可以使用其他数据库(例如MySQL,DB2,SQL Server和其他数据库)通过查询检查递归。前提是那些数据库当然支持递归with子句。
这是它在Oracle 11.2.0.1.0上的工作方式
SQL> select * 2 from t 3 / ID PARENT_ID ---------- ---------- 1 2 2 1 2 rows selected.
使用CONNECT BY语法的查询:
SQL> select id 2 , parent_id 3 , connect_by_iscycle 4 from t 5 connect by nocycle parent_id = prior id 6 start with id = 1 7 / ID PARENT_ID CONNECT_BY_ISCYCLE ---------- ---------- ------------------ 1 2 0 2 1 1 2 rows selected.
在我看来,这很直观。但是,使用新的ANSI语法,它将返回多一行:
SQL> with tr (id,parent_id) as 2 ( select id 3 , parent_id 4 from t 5 where id = 1 6 union all 7 select t.id 8 , t.parent_id 9 from t 10 join tr on t.parent_id = tr.id 11 ) cycle id set is_cycle to '1' default '0' 12 select id 13 , parent_id 14 , is_cycle 15 from tr 16 / ID PARENT_ID I ---------- ---------- - 1 2 0 2 1 0 1 2 1 3 rows selected.
这是您可以用来检查的脚本:
create table t ( id number , parent_id number ); insert into t values (1, 2); insert into t values (2, 1); commit; with tr (id,parent_id) as ( select id , parent_id from t where id = 1 union all select t.id , t.parent_id from t join tr on t.parent_id = tr.id ) cycle id set is_cycle to '1' default '0' select id , parent_id , is_cycle from tr;
从以下文档CONNECT_BY_ISCYCLE:
CONNECT_BY_ISCYCLE
该CONNECT_BY_ISCYCLE虚列返回1如果当前行有一个孩子,这也是它的祖先
1
以及CYCLE:
CYCLE
如果某一行的祖先行的某个周期列的值相同,则认为该行构成了一个周期。
在您的示例中,row2确实有一个孩子,它也是其祖先,但id尚未返回。
2
id
换句话说,CONNECT_BY_ISCYCLE检查 子项 (尚未返回),同时CYCLE检查 当前行 (已返回)。
CONNECT BY基于行,而递归CTE基于集。
CONNECT BY
CTE
请注意,Oracle的文档中CYCLE提到了“祖先行”。但是,一般来讲,递归中没有“祖先行”的概念CTE。这是一个基于集合的操作,可以完全从树中产生结果。一般来说,锚部分和递归部分甚至可以使用不同的表。
由于递归CTE的是 通常 用于建立层次结构树,Oracle决定增加一个周期的检查。但是由于递归CTE操作的基于集合的方式,通常无法判断下一步是否会生成一个循环,因为如果没有对“祖先行”循环条件的明确定义,也将无法定义。
Oracle
要执行“下一步”步骤,需要整个“当前”集可用,但是要生成当前集的每一行(包括循环列),我们只需要具有“下一步”操作的结果即可。
如果当前集合始终仅由一行组成(例如中的CONNECT BY),这不是问题,但是如果对集合进行整体定义的递归操作则是一个问题。
没考虑Oracle 11还,但SQL Server工具递归CTE的只是隐藏的CONNECT BY在他们身后,这需要将大量的限制(所有有效禁止所有基于集合的操作)。
Oracle 11
SQL Server
PostgreSQL另一方面,的实现是真正基于集合的:您可以对递归部分中的锚点部分执行任何操作。但是,它没有任何检测循环的方法,因为循环不是首先定义的。
PostgreSQL
如前所述,MySQL根本不实现CTEs(也没有实现HASH JOINs或MERGE JOINs,仅实现嵌套循环,因此不要感到惊讶)。
MySQL
HASH JOIN
MERGE JOIN
具有讽刺意味的是,我今天收到了关于这个主题的一封信,我将在我的博客中进行介绍。
更新:
递归CTE的SQL Server只不过CONNECT BY是变相而已。有关令人震惊的详细信息,请参阅我的博客中的这篇文章: