oracle遞歸函數

oracle start with connect by 用法

oracle中 connect by prior 遞歸算法 

Oracle中start with...connect by prior子句用法 connect by 是結構化查詢中用到的,其基本語法是: 
select ... from tablename start with 條件1 
connect by 條件2 
where 條件3; 
例: 
select * from table 
start with org_id = 'HBHqfWGWPy' 
connect by prior org_id = parent_id; 

     簡單說來是將一個樹狀結構存儲在一張表裏,好比一個表中存在兩個字段: 
org_id,parent_id那麼經過表示每一條記錄的parent是誰,就能夠造成一個樹狀結構。 
     用上述語法的查詢能夠取得這棵樹的全部記錄。 
     其中: 
     條件1 是根結點的限定語句,固然能夠放寬限定條件,以取得多個根結點,實際就是多棵樹。 
     條件2 是鏈接條件,其中用PRIOR表示上一條記錄,好比 CONNECT BY PRIOR org_id = parent_id就是說上一條記錄的org_id 是本條記錄的parent_id,即本記錄的父親是上一條記錄。 
     條件3 是過濾條件,用於對返回的全部記錄進行過濾。 

     簡單介紹以下: 
     早掃描樹結構表時,須要依此訪問樹結構的每一個節點,一個節點只能訪問一次,其訪問的步驟以下: 
     第一步:從根節點開始; 
     第二步:訪問該節點; 
     第三步:判斷該節點有無未被訪問的子節點,如有,則轉向它最左側的未被訪問的子節,並執行第二步,不然執行第四步; 
     第四步:若該節點爲根節點,則訪問完畢,不然執行第五步; 
     第五步:返回到該節點的父節點,並執行第三步驟。 

     總之:掃描整個樹結構的過程也便是中序遍歷樹的過程。 

     1. 樹結構的描述 
樹結構的數據存放在表中,數據之間的層次關係即父子關係,經過表中的列與列間的關係來描述,如EMP表中的EMPNO和 MGR。EMPNO表示該僱員的編號,MGR表示領導該僱員的人的編號,即子節點的MGR值等於父節點的EMPNO值。在表的每一行中都有一個表示父節點 的MGR(除根節點外),經過每一個節點的父節點,就能夠肯定整個樹結構。 
在SELECT命令中使用CONNECT BY 和善START WITH 子句能夠查詢表中的樹型結構關係。其命令格式以下: 
SELECT 。。。 
CONNECT BY {PRIOR 列名1=列名2|列名1=PRIOR 裂名2} 
[START WITH]; 
其中:CONNECT BY子句說明每行數據將是按層次順序檢索,並規定將表中的數據連入樹型結構的關係中。PRIORY 運算符必須放置在鏈接關係的兩列中某一個的前面。對於節點間的父子關係,PRIOR運算符在一側表示父節點,在另外一側表示子節點,從而肯定查找樹結構是的 順序是自頂向下仍是自底向上。在鏈接關係中,除了可使用列名外,還容許使用列表達式。START WITH 子句爲可選項,用來標識哪一個節點做爲查找樹 型結構的根節點。若該子句被省略,則表示全部知足查詢條件的行做爲根節點。 
START WITH: 不但能夠指定一個根節點,還能夠指定多個根節點。 
2. 關於PRIOR 
運算符PRIOR被放置於等號先後的位置,決定着查詢時的檢索順序。 
PRIOR被置於CONNECT BY子句中等號的前面時,則強制從根節點到葉節點的順序檢索,即由父節點向子節點方向經過樹結構,咱們稱之爲自頂向下的方式。如: 
CONNECT BY PRIOR EMPNO=MGR 
PIROR運算符被置於CONNECT BY 子句中等號的後面時,則強制從葉節點到根節點的順序檢索,即由子節點向父節點方向經過樹結構,咱們稱之爲自底向上的方式。例如: 
CONNECT BY EMPNO=PRIOR MGR 
在這種方式中也應指定一個開始的節點。 
3. 定義查找起始節點 
     在自頂向下查詢樹結構時,不但能夠從根節點開始,還能夠定義任何節點爲起始節點,以此開始向下查找。這樣查找的結果就是以該節點爲開始的結構樹的一枝。 
4.使用LEVEL 
在具備樹結構的表中,每一行數據都是樹結構中的一個節點,因爲節點所處的層次位置不一樣,因此每行記錄均可以有一個層號。層號根據節點與根節點的距離肯定。不論從哪一個節點開始,該起始根節點的層號始終爲1,根節點的子節點爲2, 依此類推。 
5.節點和分支的裁剪 
在對樹結構進行查詢時,能夠去掉表中的某些行,也能夠剪掉樹中的一個分支,使用WHERE子句來限定樹型結構中的單個節點,以去掉樹中的單個節點,但它卻不影響其後代節點(自頂向下檢索時)或前輩節點(自底向頂檢索時)。 
6.排序顯示 
象在其它查詢中同樣,在樹結構查詢中也可使用ORDER BY 子句,改變查詢結果的顯示順序,而沒必要按照遍歷樹結構的順序 
算法

----------------------------------------------------------------------------------------------------------sql

oracle 提供了start with connect by 語法結構能夠實現遞歸查詢。

1. 一個簡單舉例:
SQL> select *  from test;

BILL_MONTH           DAY_NUMBER MSISDN
-------------------- ---------- --------------------
200803                        1 13800
200803                        3 13800
200803                        2 13800
200803                        2 13801
200803                        4 13804
200803                        5 13804
200803                        7 13804
200803                        8 13804
200803                        6 13802
200803                        6 13801
200803                        7 13801
200803                        8 13801

12 rows selected

SQL>
SQL> select * from test
2       start with day_number=1
3       connect by  prior day_number=day_number-1 and prior msisdn= msisdn
4      ;

BILL_MONTH           DAY_NUMBER MSISDN
-------------------- ---------- --------------------
200803                        1 13800
200803                        2 13800
200803                        3 13800

SQL>


上面的語句查找出了從1開始,而且day_number 逐漸+1 遞增的,而且 msisdn 相同的哪些個數據.


2. start with  connect by 語法結構
如上面說看到的 例子, 其語法結構爲  start with condition  connect by  condition (含 prior 關鍵字)
start with conditon 給出的seed 數據的範圍, connect by  後面給出了遞歸查詢的條件,prior 關鍵字表示父數據,prior 條件表示子數據須要知足父數據的什麼條件。

在下面的這個start with connect by 結構中,就表示 查找出了從1開始,而且day_number 逐漸+1 遞增的,而且 msisdn 相同的那些個數據.

start with day_number=1
connect by  prior day_number=day_number-1 and prior msisdn= msisdn

3.  執行計劃
對於這個特殊的語法結構,咱們來看看它的執行計劃。
經過下面的執行計劃,咱們能夠看出,對於簡單的訪問一個對象的遞歸查詢,實際上oracle 要三次訪問要查詢的對象。所以,這一個告訴咱們,在使用遞歸查詢時,必定要謹慎,由於即便原表數據很少,可是三倍的訪問喜好來,代價也會很大。

SQL> explain plan for

2   select * from  test
3    --where  bill_month='200803'
4    start with day_number=1
5    connect by  prior day_number=day_number-1 and prior msisdn= msisdn
6  ;

Explained

SQL> select *  from  table( dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
-------------------------------------------------------------------------
| Id  | Operation                 |  Name       | Rows  | Bytes | Cost  |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT          |             |       |       |       |
|*  1 |  CONNECT BY WITH FILTERING|             |       |       |       |
|*  2 |   FILTER                  |             |       |       |       |
|   3 |    TABLE ACCESS FULL      | TEST        |       |       |       |
|   4 |   NESTED LOOPS            |             |       |       |       |
|   5 |    BUFFER SORT            |             |       |       |       |
|   6 |     CONNECT BY PUMP       |             |       |       |       |
|*  7 |    TABLE ACCESS FULL      | TEST        |       |       |       |
|   8 |   TABLE ACCESS FULL       | TEST        |       |       |       |
-------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("TEST"."DAY_NUMBER"=1)
2 - filter("TEST"."DAY_NUMBER"=1)

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
7 - filter("TEST"."MSISDN"=NULL AND "TEST"."DAY_NUMBER"-1=NULL)
Note: rule based optimization

23 rows selected

SQL>


另外,發現了在含有其餘條件的遞歸中,是先處理全部的遞歸查詢,最後才用加入的條件過濾.
請看下面的例子。
和上面的執行計劃對比下咱們能夠知道,加入條件   where  bill_month='200803' 後,實際上倒是在遞歸完成後,最後才執行的    1 - filter("TEST"."BILL_MONTH"='200803') 。

因此,爲了確保語句的性能,不要直接加入條件在start with connect by 結構中,而是要想辦法將原表的數據控制住。這個能夠採用子查 詢的辦法,或者使用臨時表等(最好採用臨時表,將數據量從本源上控制住;由於從子查詢的執行計劃咱們能夠看到,它每次也都是訪問全表,再用條件過濾,要重 復三次,不是一次過濾就夠了).

--直接加入條件後的執行計劃
SQL> explain plan for

2   select * from  test
3    where  bill_month='200803'
4    start with day_number=1
5    connect by  prior day_number=day_number-1 and prior msisdn= msisdn
6  ;

Explained

SQL> select *  from  table( dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
--------------------------------------------------------------------------
| Id  | Operation                  |  Name       | Rows  | Bytes | Cost  |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |             |       |       |       |
|*  1 |  FILTER                    |             |       |       |       |
|*  2 |   CONNECT BY WITH FILTERING|             |       |       |       |
|*  3 |    FILTER                  |             |       |       |       |
|   4 |     TABLE ACCESS FULL      | TEST        |       |       |       |
|   5 |    NESTED LOOPS            |             |       |       |       |
|   6 |     BUFFER SORT            |             |       |       |       |
|   7 |      CONNECT BY PUMP       |             |       |       |       |
|*  8 |     TABLE ACCESS FULL      | TEST        |       |       |       |
|   9 |    TABLE ACCESS FULL       | TEST        |       |       |       |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("TEST"."BILL_MONTH"='200803')

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
2 - filter("TEST"."DAY_NUMBER"=1)
3 - filter("TEST"."DAY_NUMBER"=1)
8 - filter("TEST"."MSISDN"=NULL AND "TEST"."DAY_NUMBER"-1=NULL)
Note: rule based optimization

25 rows selected

SQL>


--使用子查詢,將過濾條件嵌在子查詢中
SQL> explain plan for

2  select * from (select * from test
3        where  bill_month='200803')
4       start with day_number=1
5       connect by  prior day_number=day_number-1 and prior msisdn= msisdn
6      ;

Explained

SQL> select *  from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
-------------------------------------------------------------------------
| Id  | Operation                 |  Name       | Rows  | Bytes | Cost  |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT          |             |       |       |       |
|*  1 |  CONNECT BY WITH FILTERING|             |       |       |       |
|*  2 |   FILTER                  |             |       |       |       |
|*  3 |    TABLE ACCESS FULL      | TEST        |       |       |       |
|   4 |   NESTED LOOPS            |             |       |       |       |
|   5 |    BUFFER SORT            |             |       |       |       |
|   6 |     CONNECT BY PUMP       |             |       |       |       |
|*  7 |    TABLE ACCESS FULL      | TEST        |       |       |       |
|*  8 |   TABLE ACCESS FULL       | TEST        |       |       |       |
-------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("TEST"."DAY_NUMBER"=1)
2 - filter("TEST"."DAY_NUMBER"=1)

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
3 - filter("TEST"."BILL_MONTH"='200803')
7 - filter("TEST"."BILL_MONTH"='200803' AND "TEST"."MSISDN"=NULL AND
"TEST"."DAY_NUMBER"-1=NULL)
8 - filter("TEST"."BILL_MONTH"='200803')
Note: rule based optimization

26 rows selected

SQL>



4. 實際中 遞歸查詢的使用。

問題:
數據庫裏有字段day_number,msisdn。如何寫月度連續3天有記錄的手機號?表結構以下:

id   bill_month   day_number     msisdn
1      200803      1           13800000000
2      200803      1           130137.....
3      200803      2           13800000000
4      200803      3           13800000000
..............................

表中3月份連續3天有記錄的紀錄就是1380000000。請問如何寫這樣的sql?


解決方案:
SQL> create  table   test ( bill_month varchar2(20),day_number number ,msisdn varchar2(20));

Table created

SQL> insert into  test values ( '200803',1,'13800');

1 row inserted

SQL> insert into  test values ( '200803',3,'13800');

1 row inserted

SQL> insert into  test values ( '200803',2,'13800');

1 row inserted

SQL> insert into  test values ( '200803',2,'13801');

1 row inserted

SQL> insert into  test values ( '200803',4,'13804');

1 row inserted

SQL> insert into  test values ( '200803',5,'13804');

1 row inserted

SQL> commit;

Commit complete

SQL> select *  from test;

BILL_MONTH           DAY_NUMBER MSISDN
-------------------- ---------- --------------------
200803                        1 13800
200803                        3 13800
200803                        2 13800
200803                        2 13801
200803                        4 13804
200803                        5 13804

6 rows selected

SQL>
SQL> select distinct  msisdn  from test  a
2  where  bill_month='200803'
3  and exists
4  ( select msisdn from  test
5    where  bill_month='200803' and msisdn=a.msisdn
6    start with day_number=a.day_number
7    connect by  prior day_number=day_number-1 and prior msisdn= msisdn
8    group by msisdn
9    having count(*)>=3
10    );

MSISDN
--------------------
13800


SQL> select *  from test;

BILL_MONTH           DAY_NUMBER MSISDN
-------------------- ---------- --------------------
200803                        1 13800
200803                        3 13800
200803                        2 13800
200803                        2 13801
200803                        4 13804
200803                        5 13804

6 rows selected

SQL> insert into  test values ( '200803',7,'13804');

1 row inserted

SQL> insert into  test values ( '200803',8,'13804');

1 row inserted

SQL> insert into  test values ( '200803',6,'13802');

1 row inserted

SQL> insert into  test values ( '200803',6,'13801');

1 row inserted

SQL> insert into  test values ( '200803',7,'13801');

1 row inserted

SQL> insert into  test values ( '200803',8,'13801');

1 row inserted

SQL> select *  from test;

BILL_MONTH           DAY_NUMBER MSISDN
-------------------- ---------- --------------------
200803                        1 13800
200803                        3 13800
200803                        2 13800
200803                        2 13801
200803                        4 13804
200803                        5 13804
200803                        7 13804
200803                        8 13804
200803                        6 13802
200803                        6 13801
200803                        7 13801
200803                        8 13801

12 rows selected

SQL> commit;

Commit complete

SQL>
SQL> select distinct  msisdn  from test  a
2  where  bill_month='200803'
3  and exists
4  ( select msisdn from  test
5    where  bill_month='200803' and msisdn=a.msisdn
6    start with day_number=a.day_number
7    connect by  prior day_number=day_number-1 and prior msisdn= msisdn
8    group by msisdn
9    having count(*)>=3
10    );

MSISDN
--------------------
13800
13801

SQL>數據庫

相關文章
相關標籤/搜索