如何在 Oracle 中使用 WITH 而不是 CONNECT BY 获得递归 SQL 的深度?

How to get depth of recursive SQL using WITH rather than CONNECT BY in Oracle?

这个简单的例子table将说明递归关系...

CREATE TABLE tree
    (
    key NUMBER(5) NOT NULL PRIMARY KEY,
    name VARCHAR(15) NOT NULL,
    treeHier NUMBER(5)
    );

INSERT INTO tree VALUES('11','Software',NULL);
INSERT INTO tree VALUES('22','OS','11');
INSERT INTO tree VALUES('33','Linux','22');
INSERT INTO tree VALUES('44','Windows','22');
INSERT INTO tree VALUES('55','DB','11');
INSERT INTO tree VALUES('66','Oracle','55');
INSERT INTO tree VALUES('77','MS-SQL','55');

COMMIT;

使用 Oracle 12c 和许多版本都有的功能,我可以使用 CONNECT BY 和 START WITH 进行递归查询以获取所有子节点。

SELECT
    LPAD(' ',LEVEL*2,' ')||' '||
    name||' '||
    key||' '||
    NVL(TO_CHAR(treeHier),'NULL')
FROM
    tree
START WITH
    key = 11
CONNECT BY
    treeHier = PRIOR key
ORDER BY
    key ASC;

伪列 LEVEL 可以了解递归的深度并缩进结果。

   Software 11 NULL
     OS 22 11
       Linux 33 22
       Windoze 44 22
     DB 55 11
       Oracle 66 55
       MS-SQL 77 55

7 rows selected.

我相信从 11g 开始,Oracle 就允许编码人员使用 WITH 子句来创建递归查询。

WITH myRecurse(key,name,treeHier) AS
    (
        SELECT
            tree.key, tree.name, tree.treeHier
        FROM
            tree
        WHERE
            tree.key = 11
    UNION ALL
        SELECT
            tree.key, tree.name, tree.treeHier
        FROM
            tree
        JOIN
            myRecur ON tree.treeHier= myRecurse.key
        ORDER BY
            3 DESC
    )
SELECT
    name||' '||
    key||' '||
    NVL(TO_CHAR(treeHier),'NULL')
FROM
    myRecurse;

但是,LEVEL 伪列似乎无法缩进输出。

Software 11 NULL
OS 22 11
DB 55 11
Linux 33 22
Windoze 44 22
Oracle 66 55
MS-SQL 77 55

7 rows selected.

是否有使用 LEVEL 或其他替代方法的方法,以便我可以使用 WITH 子句缩进并获得类似于 CONNECT BY、START WITH 方法的输出?

以下是如何克隆 LEVEL 伪列(用于缩进)以及在使用递归 WITH 子句时分层 CONNECT BY 查询的深度优先顺序。您可能想看看当您删除递归子查询后的 SEARCH... 子句和末尾的 ORDER BY 子句时会发生什么 - 查看输出受到何种影响。

with r ( lvl, key, name, treehier ) as (
         select  1, key, name, treehier
           from  tree
           where treehier is null
         union all
         select  r.lvl + 1, t.key, t.name, r.key
         from    r join tree t on r.key = t.treehier
       )
       search depth first by name set ord
select   rpad(' ', 2 * (lvl - 1), ' ') || name as name, key, treehier
from     r
order by ord
;

NAME                                  KEY   TREEHIER
------------------------------ ---------- ----------
Software                               11           
  DB                                   55         11
    MS-SQL                             77         55
    Oracle                             66         55
  OS                                   22         11
    Linux                              33         22
    Windows                            44         22

然后您可以在 ORDER BY 子句中添加关键字 DESC(用于降序排序,而不是默认的升序排序)- 在 ord 之后(几乎肯定不是您想要的!),或者在删除之后ORDER BY 子句中的 DESC,将其添加到 SEARCH 子句中的 name 之后。您将看到在输出中添加 DESC 关键字的位置有何不同。如果需要,这些都是您将来可以使用的选项。您可能还想尝试在 SEARCH 子句中使用 KEY 而不是 NAME - 看看会导致什么变化。