Tags

, , , ,

How to implement CONNECT BY features

ORACLE 11g release 2 introduced the recursive WITH clause as an alternative to the well known CONNECT BY clause.

This Blog entry shows how to implement all the CONNECT BY features using the new recursive WITH clause.


Leaf Nodes

Using CONNECT BY the pseudo column CONNECT_BY_ISLEAF can be used to determine whether a node is a leaf node (has no children) or an inner node. The value of the pseudo column CONNECT_BY_ISLEAF is “0” for an inner node and “1” for leaf nodes.

SELECT LEVEL, ename, CONNECT_BY_ISLEAF leaf, empno, mgr
  FROM emp e
 WHERE deptno = 10
CONNECT BY PRIOR empno = mgr
 START WITH mgr is NULL
 ORDER SIBLINGS BY ename
/

LEVEL      ENAME      LEAF       EMPNO      MGR
---------- ---------- ---------- ---------- ----------
         1 KING                0       7839
         2 CLARK               0       7782       7839
         3 MILLER              1       7934       7782

Recursive WITH clause does not offer a built-in function that provides this information. Easiest way to get the information is using a subquery (in-/outside the WITH clause) that counts the number of child nodes.

WITH emps (ename, empno, mgr, lvl)
       AS (SELECT ename, empno, mgr, 1 AS lvl
             FROM emp
            WHERE mgr IS NULL
           UNION ALL
           SELECT e.ename, e.empno, e.mgr, m.lvl + 1 AS lvl
             FROM            emps m
                  INNER JOIN emp  e ON (e.mgr = m.empno)
            WHERE e.deptno = 10)
SEARCH DEPTH FIRST BY ename SET sorting
SELECT e.lvl, e.ename
     , 1 - LEAST((SELECT COUNT(*) FROM emp x WHERE x.mgr = e.empno),1) leaf
     , e.empno, e.mgr
  FROM emps e
 ORDER BY e.sorting
/

LEVEL      ENAME       LEAF      EMPNO       MGR
---------- ---------- ---------- ---------- ----------
         1 KING                0       7839
         2 CLARK               0       7782       7839
         3 MILLER              1       7934       7782

If the SEARCH clause uses DEPTH_FIRST, the analytic function LEAD can be used to determine whether the column LVL of next row in the result set has a higher value than the current row. If so, the current row is not a leaf otherwise the current row is a leaf.

WITH emps (ename, empno, mgr, lvl)
       AS (SELECT ename, empno, mgr, 1 AS lvl
             FROM emp
            WHERE mgr IS NULL
           UNION ALL
           SELECT e.ename, e.empno, e.mgr, m.lvl + 1 AS lvl
             FROM            emps m
                  INNER JOIN emp  e ON (e.mgr = m.empno)
            WHERE e.deptno = 10)
SEARCH DEPTH FIRST BY ename SET sortingSELECT lvl
      ,ename
      ,CASE
          WHEN lvl - LEAD(lvl) OVER (ORDER BY sorting) < 0 
             THEN 0 
          ELSE 1 
       END AS leaf 
     ,empno
     ,mgr 
  FROM emps 
 ORDER BY sorting 
/ 

LVL        ENAME      LEAF       EMPNO      MGR 
---------- ---------- ---------- ---------- ---------- 
         1 KING                0       7839 
         2 CLARK               0       7782       7839 
         3 MILLER              1       7934       7782