程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> Oracle數據庫 >> Oracle教程 >> Oracle 遞歸

Oracle 遞歸

編輯:Oracle教程

  當對象存在父節點、子節點時,通過特定的方式獲取父節點、子節點數據構建樹狀結構或其它形式結構時,通常都會使用遞歸,如:一個公司有多個部門、每個部門下可能有多個小部門,小部門下面又有組….為了數據容易管理和維護,通過構建合適的表結構存儲這些數據,以下示例以省市縣為例學習了解遞歸:
1.創建存儲省市縣數據表:

   1:  create table tb_distree
   2:  (
   3:  id number,
   4:  name varchar2(300),
   5:  pid number
   6:  )
   7:  /
   8:   remark 添加主外鍵
   9:  alter table tb_distree add (
  10:  constraints pk_id primary key(id),
  11:  constraints fk_pid foreign key(pid) references tb_distree(id)
  12:  )
  13:  /

2.初始化數據:

   1:  insert into tb_distree(id,name) values(1,'雲南省');
   2:  insert into tb_distree(id,name,pid) values(2,'昆明市',1);
   3:  insert into tb_distree(id,name,pid) values(3,'臨滄市',1);
   4:  insert into tb_distree(id,name,pid) values(4,'麗江市',1);
   5:  insert into tb_distree(id,name,pid) values(5,'雲縣',3);
   6:  insert into tb_distree(id,name,pid) values(6,'鳳慶',3);
   7:  insert into tb_distree(id,name,pid) values(7,'幸福',3);
   8:  insert into tb_distree(id,name,pid) values(8,'盤龍區',2);
   9:  insert into tb_distree(id,name,pid) values(9,'五華區',2);
  10:  insert into tb_distree(id,name,pid) values(10,'西山區',2);

3.遞歸查詢語法:

   1:  select column... from table_name 
   2:  where ....  過濾條件
   3:  start with ...  遞歸開始點
   4:  connect by prior .... 優先級

4.遞歸查詢數據:
4.1 從父節點開始查詢出所有父節點和子節點:

SQL> select id,name,pid from tb_distree start with pid is null connect by prior id=pid;
 
        ID NAME                PID
---------- ------------ ----------
         1 雲南省
         2 昆明市                1
         8 盤龍區                2
         9 五華區                2
        10 西山區                2
         3 臨滄市                1
         5 雲縣                  3
         6 鳳慶                  3
         7 幸福                  3
         4 麗江市                1
 
10 rows selected.

4.2 查詢某個節點的父節點:

 SQL> select id,name,pid from tb_distree start with name='雲縣' connect by prior pid=id;
 
        ID NAME                PID
---------- ------------ ----------
         5 雲縣                  3
         3 臨滄市                1
         1 雲南省

在上例中"雲縣"屬於"臨滄市","臨滄市"屬於"雲南省";對於從父節點遞歸到子節點,優先級條件為子節點id等於父節點id;對於從子節點到父節點遞歸,方向剛好相反;
4.3 通過層次查詢出父節點和某個子節點:

SQL> select id,name,pid,level from tb_distree where level in(1,2) start with pid is null connect by prior id=pid;
 
        ID NAME                PID      LEVEL
---------- ------------ ---------- ----------
         1 雲南省                           1
         2 昆明市                1          2
         3 臨滄市                1          2
         4 麗江市                1          2

層次也很重要,某些時候要修改某個節點父節點或子節點時會很有用;
5. 遞歸查詢效率:    

SQL>select/*+ selectDG1 */ id,name,pid from tb_distree start with pid is null connect by prior id=pid;
 
SQL> select sql_id,sql_text from v$sql where sql_text like '%selectDG1%' ;    
SQL_ID          SQL_TEXT
--------------- --------------------------------------------------
2wnu324ga4n0y   select sql_id,sql_text from v$sql where sql_text l
                ike '%selectDG1%'
 
d4g89bucsbvzd   select/*+ selectDG1 */ id,name,pid from tb_distree
                 start with pid is null connect by prior id=pid
 
         
SQL>select * from table(dbms_xplan.display_cursor('d4g89bucsbvzd',null,'advanced allstats last peeked_binds'));
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------
SQL_ID  d4g89bucsbvzd, child number 0
-------------------------------------
select/*+ selectDG1 */ id,name,pid from tb_distree start with pid is
null connect by prior id=pid
 
Plan hash value: 1466399788
 
------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                               | Name       | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                        |            |      1 |        |       |    18 (100)|          |     10 |00:00:00.01 |      22 |
 
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------
|*  1 |  CONNECT BY NO FILTERING WITH START-WITH|            |      1 |        |       |            |          |     10 |00:00:00.01 |      22 |
|   2 |   TABLE ACCESS FULL                     | TB_DISTREE |      1 |     10 |  1780 |    17   (0)| 00:00:01 |     10 |00:00:00.01 |      22 |
------------------------------------------------------------------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$1
   2 - SEL$2 / TB_DISTREE@SEL$2
 
Outline Data
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('11.2.0.3')
      DB_VERSION('11.2.0.3')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$2")
      OUTLINE_LEAF(@"SEL$3")
      OUTLINE_LEAF(@"SEL$4")
 
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------------------------
      OUTLINE_LEAF(@"SET$1")
      OUTLINE_LEAF(@"SEL$1")
      NO_ACCESS(@"SEL$1" "connect$_by$_work$_set$_006"@"SEL$1")
      NO_CONNECT_BY_FILTERING(@"SEL$1")
      CONNECT_BY_COMBINE_SW(@"SEL$1")
      FULL(@"SEL$4" "TB_DISTREE"@"SEL$4")
      FULL(@"SEL$3" "connect$_by$_pump$_002"@"SEL$3")
      FULL(@"SEL$3" "TB_DISTREE"@"SEL$3")
      LEADING(@"SEL$3" "connect$_by$_pump$_002"@"SEL$3" "TB_DISTREE"@"SEL$3")
      USE_HASH(@"SEL$3" "TB_DISTREE"@"SEL$3")
      FULL(@"SEL$2" "TB_DISTREE"@"SEL$2")
 
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------------------
      END_OUTLINE_DATA
  */
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - access("PID"=PRIOR NULL)
       filter("PID" IS NULL)
       
 10046 trace:
Rows (1st) Rows (avg) Rows (max)  Row Source Operation
 675 ---------- ---------- ----------  ---------------------------------------------------
 676         10         10         10  CONNECT BY NO FILTERING WITH START-WITH (cr=22 pr=0 pw=0 time=269 us)
 677         10         10         10   TABLE ACCESS FULL TB_DISTREE (cr=22 pr=0 pw=0 time=118 us cost=17 size=1780 card=10)
 678 
 679 
 680 Elapsed times include waiting on following events:
 681   Event waited on                             Times   Max. Wait  Total Waited
 682   ----------------------------------------   Waited  ----------  ------------
 683   row cache lock                                  3        0.00          0.00
 684   Disk file operations I/O                        1        0.00          0.00
 685   db file sequential read                         3        0.03          0.04
 686   SQL*Net message to client                       2        0.00          0.00
 687   SQL*Net message from client                     2        0.00          0.00                                                                                                      
 688 ********************************************************************************              

在遞歸表上創建索引是沒有用的,都必須進行全表掃描,當然通常這樣的表也不會很大,如果數據量的確很大,建議將表中的節點拆分成多個表提高效率;

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved