• 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 ********************************************************************************              

    在递归表上创建索引是没有用的,都必须进行全表扫描,当然通常这样的表也不会很大,如果数据量的确很大,建议将表中的节点拆分成多个表提高效率;

  • 相关阅读:
    Android 入门到精通 (Index)
    负载平衡与冗余备份方案概述
    Android 程序组件交互分析
    复制时保留文件的目录结构
    notepad++中设置tab缩进的宽度
    scws
    php 将字符(包括汉字) 转换成16进制 (apache access log 中文显示16进制码)
    批量修改完整版本
    根据端口号查进程
    php性能优化
  • 原文地址:https://www.cnblogs.com/lanston/p/3750552.html
Copyright © 2020-2023  润新知