会计考友 发表于 2012-8-4 13:54:49

Oracle辅导:Oracle中10g树形查询新特性

在10g中Oracle提供了新的伪列:CONNECT_BY_ISCYCLE,通过这个伪列,可以判断是否在树形查询的过程中构成了循环,这个伪列只是在CONNECT BY NOCYCLE方式下有效。   这一篇描述一下解决问题的思路。
  CONNECT_BY_ISCYCLE的实现和前面两篇文章中CONNECT_BY_ROOT和CONNECT_BY_ISLEAF的实现完全不同。
  因为要实现CONNECT_BY_ISCYCLE,就必须先实现CONNECT BY NOCYCLE,而在9i中是没有方法实现这个功能的。
  也就是说,首先要实现自己的树形查询的功能,而仅这第一点,就是一个异常困难的问题,何况后面还要实现NOCYCLE,最后再加上一个ISCYCLE的判断。
  所以总的来说,这个功能的实现比前面两个功能要复杂得多。由于树形查询的LEVEL是不固定的,所以采用链接的方式实现,基本上是不现实的。换句话说,用纯SQL的方式来实现树形查询的功能基本上不可行。而为了解决这个功能,只能通过PL/SQL配合SQL来实现。
  仍然是首先构造一个例子:
  SQL> CREATE TABLE T_TREE (ID NUMBER, FATHER_ID NUMBER, NAME VARCHAR2(30));
  表已创建。
  SQL> INSERT INTO T_TREE VALUES (1, 0, 'A');
  已创建 1 行。
  SQL> INSERT INTO T_TREE VALUES (2, 1, 'BC');
  已创建 1 行。
  SQL> INSERT INTO T_TREE VALUES (3, 1, 'DE');
  已创建 1 行。
  SQL> INSERT INTO T_TREE VALUES (4, 1, 'FG');
  已创建 1 行。
  SQL> INSERT INTO T_TREE VALUES (5, 2, 'HIJ');
  已创建 1 行。
  SQL> INSERT INTO T_TREE VALUES (6, 4, 'KLM');
  已创建 1 行。
  SQL> INSERT INTO T_TREE VALUES (7, 6, 'NOPQ');
  已创建 1 行。
  SQL> INSERT INTO T_TREE VALUES (0, 0, 'ROOT');
  已创建 1 行。
  SQL> INSERT INTO T_TREE VALUES (4, 7, 'FG');
  已创建 1 行。
  SQL> COMMIT;
  提交完成。
  SQL> SELECT * FROM T_TREE;
  ID FATHER_ID NAME
  ---------- ---------- ------------------------------
  1 0 A
  2 1 BC
  3 1 DE
  4 1 FG
  5 2 HIJ
  6 4 KLM
  7 6 NOPQ
  0 0 ROOT
  4 7 FG

  已选择9行。

会计考友 发表于 2012-8-4 13:54:50

Oracle辅导:Oracle中10g树形查询新特性

</p>  上面构造了两种树形查询循环的情况,一种是当前记录的自循环,另一种是树形查询的某个子节点是当前节点的祖先节点,从而构成了循环。在这个例子中,记录ID为0和ID为4且FATHER_ID等于7的两条记录分别构成了上述的两种循环的情况。
  下面就来看看CONNECT_BY_ISCYCLE和CONNECT BY NOCYCLE的功能:
  SQL> SELECT *
  2 FROM T_TREE
  3 START WITH ID = 0
  4 CONNECT BY PRIOR ID = FATHER_ID;
  ERROR:
  ORA-01436: 用户数据中的 CONNECT BY 循环
  未选定行
  SQL> SELECT *
  2 FROM T_TREE
  3 START WITH ID = 1
  4 CONNECT BY PRIOR ID = FATHER_ID;
  ERROR:
  ORA-01436: 用户数据中的 CONNECT BY 循环
  未选定行
  这就是不使用CONNECT BY NOCYCLE的情况,查询会报错,指出树形查询中出现循环,在10g中可以使用CONNECT BY NOCYCLE的方式来避免错误的产生:
  SQL> SELECT *
  2 FROM T_TREE
  3 START WITH ID = 0
  4 CONNECT BY NOCYCLE PRIOR ID = FATHER_ID;
  ID FATHER_ID NAME
  ---------- ---------- ------------------------------
  0 0 ROOT
  1 0 A
  2 1 BC
  5 2 HIJ
  3 1 DE
  4 1 FG
  6 4 KLM
  7 6 NOPQ
  已选择8行。
  使用CONNECT BY NOCYCLE,Oracle自动避免循环的产生,将不产生循环的数据查询出来,下面看看CONNECT_BY_ISCYCLE的功能:
  SQL> SELECT ID,
  2 FATHER_ID,
  3 NAME,
  4 CONNECT_BY_ISCYCLE CYCLED
  5 FROM T_TREE
  6 START WITH ID = 0
  7 CONNECT BY NOCYCLE PRIOR ID = FATHER_ID;
  ID FATHER_ID NAME CYCLED
  ---------- ---------- ------------------------------ ----------
  0 0 ROOT 1
  1 0 A 0
  2 1 BC 0
  5 2 HIJ 0
  3 1 DE 0
  4 1 FG 0
  6 4 KLM 0
  7 6 NOPQ 1
  已选择8行。

  可以看到,CONNECT_BY_ISCYCLE伪列指出循环在树形查询中发生的位置。

会计考友 发表于 2012-8-4 13:54:51

Oracle辅导:Oracle中10g树形查询新特性

</p>  为了实现CONNECT_BY_ISCYCLE就必须先实现CONNECT BY NOCYCLE方式,而这在9i中是没有现成的办法的,所以这里尝试使用PL/SQL来自己实现树形查询的功能。
  SQL> CREATE OR REPLACE FUNCTION F_FIND_CHILD(P_VALUE VARCHAR2) RETURN VARCHAR2 AS
  2 V_STR VARCHAR2(32767) := '/' || P_VALUE;
  3
  4 PROCEDURE P_GET_CHILD_STR (P_FATHER IN VARCHAR2, P_STR IN OUT VARCHAR2) AS
  5 BEGIN
  6 FOR I IN (SELECT ID FROM T_TREE WHERE FATHER_ID = P_FATHER AND FATHER_ID != ID) LOOP
  7 IF INSTR(P_STR || '/', '/' || I.ID || '/') = 0 THEN
  8 P_STR := P_STR || '/' || I.ID;
  9 P_GET_CHILD_STR(I.ID, P_STR);
  10 END IF;
  11 END LOOP;
  12 END;
  13 BEGIN
  14 P_GET_CHILD_STR(P_VALUE, V_STR);
  15 RETURN V_STR;
  16 END;
  17 /
  函数已创建。
  构造一个函数,在函数中递归调用过程来实现树形查询的功能。
  下面看看调用这个函数的结果:
  SQL> SELECT F_FIND_CHILD(0) FROM DUAL;
  F_FIND_CHILD(0)
  ------------------------------------------------
  /0/1/2/5/3/4/6/7
  SQL> SELECT F_FIND_CHILD(2) FROM DUAL;
  F_FIND_CHILD(2)
  ------------------------------------------------
  /2/5
  SQL> SELECT F_FIND_CHILD(4) FROM DUAL;
  F_FIND_CHILD(4)
  ------------------------------------------------
  /4/6/7
  虽然目前存在的问题还有很多,但是已经基本上实现了一个最简单的NOCYCLE的SYS_CONNECT_BY_PATH的功能。
  有了这个函数作为基础,就可以逐步的实现最终的目标了。
页: [1]
查看完整版本: Oracle辅导:Oracle中10g树形查询新特性