10g樹形查詢新特性CONNECT_BY_ISCYCLE的9i實現方式(一)

yangtingkun發表於2008-07-20

10gOracle提供了新的偽列:CONNECT_BY_ISCYCLE,透過這個偽列,可以判斷是否在樹形查詢的過程中構成了迴圈,這個偽列只是在CONNECT BY NOCYCLE方式下有效。

這一篇描述一下解決問題的思路。

 

 

CONNECT_BY_ISCYCLE的實現和前面兩篇文章中CONNECT_BY_ROOTCONNECT_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行。

上面構造了兩種樹形查詢迴圈的情況,一種是當前記錄的自迴圈,另一種是樹形查詢的某個子節點是當前節點的祖先節點,從而構成了迴圈。在這個例子中,記錄ID0ID4FATHER_ID等於7的兩條記錄分別構成了上述的兩種迴圈的情況。

下面就來看看CONNECT_BY_ISCYCLECONNECT 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 NOCYCLEOracle自動避免迴圈的產生,將不產生迴圈的資料查詢出來,下面看看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偽列指出迴圈在樹形查詢中發生的位置。

為了實現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

雖然目前存在的問題還有很多,但是已經基本上實現了一個最簡單的NOCYCLESYS_CONNECT_BY_PATH的功能。

有了這個函式作為基礎,就可以逐步的實現最終的目標了。

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4227/viewspace-405453/,如需轉載,請註明出處,否則將追究法律責任。

相關文章