现在的位置: 首页 > 综合 > 正文

oracle 树形查找

2018年05月24日 ⁄ 综合 ⁄ 共 11132字 ⁄ 字号 评论关闭

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, 依此类推。图1.2就表示了树结构的层次。
  5.节点和分支的裁剪
  在对树结构进行查询时,可以去掉表中的某些行,也可以剪掉树中的一个分支,使用WHERE子句来限定树型结构中的单个节点,以去掉树中的单个节点,但它却不影响其后代节点(自顶向下检索时)或前辈节点(自底向顶检索时)。
  6.排序显示
  象在其它查询中一样,在树结构查询中也可以使用ORDER BY 子句,改变查询结果的显示顺序,而不必按照遍历树结构的顺序。
  ===================补充===================
  Start with...Connect By子句递归查询一般用于一个表维护树形结构的应用。
  创建示例表:
  CREATE TABLE TBL_TEST
  (
   ID NUMBER,
   NAME VARCHAR2(100 BYTE),
   PID NUMBER DEFAULT 0
  );
  插入测试数据:
  INSERT INTO TBL_TEST(ID,NAME,PID) VALUES('1','10','0');
  INSERT INTO TBL_TEST(ID,NAME,PID) VALUES('2','11','1');
  INSERT INTO TBL_TEST(ID,NAME,PID) VALUES('3','20','0');
  INSERT INTO TBL_TEST(ID,NAME,PID) VALUES('4','12','1');
  INSERT INTO TBL_TEST(ID,NAME,PID) VALUES('5','121','2');
  从Root往树末梢递归
  select * from TBL_TEST
  start with id=1
  connect by prior id = pid
  从末梢往树ROOT递归
  select * from TBL_TEST
  start with id=5

  connect by prior pid = id



文章分类:数据库 Oracle是一种关系型数据库,在表中不可能以层次的关系存放数据,但是oracle提供了层次(树形)查询语句,使用树的遍历来获得层次关系的数据。 



sql语法 

select column,expr…. 

From table 

Where conditions 

Start with conditions 

Connect by prior conditions 



关键字和伪列介绍 

Select 

部分可以是字段或者表达式,或者伪列,如level,connect_by_isleaf等. 

From 

From后面可以是table,view但是只能是一个table,view中不能有多个表连接. 

Where 

条件限制了查询返回的行,但是其只影响节点自身,该节点的下层child不受影响 

,属于节点的截断. 

start with 

确定遍历查询的开始点,可以是子查询,也可以不指定,不指定表示每个节点都作为起始节点来遍历一遍 

connect by prior 

确定遍历的方向,即是找子孙节点还是找祖先节点,遍历方向分为:自上向下,自下向上. 

如果prior字段放在父字段前面,则表示要做自下向上的遍历; 

如果prior字段放在子字段段前面,则表示要做自上向下的遍历; 

和关键字prior放在=号左边右边没关系。 



父字段:指明记录上一节点的字段如emp表中的mgr字段; 

子字段:用来和父字段连接的字段,如emp标中的empno字段 

level  

查询的起始节点level为1,子孙依次增加,代表每个节点在家族树中的层次关系 

connect_by_isleaf  

是否叶子节点,如果查询时自顶向下,则叶子节点为1,如果自下向上,则根节点为1 

connect _by_root column  

查找子节点,叶子节点对应的根节点,10g新增的,这个很有用 

prior 

表示父节点,可以用在selelct部分,也可以用在connect by部分 

nocycle                  

如果出现循环,在connect by中制定nocycle选项,查询将跳过循环部分的节点,避免10g之前的抱错。 

connect_iscycle  

如果出现循环,则为1,可以找出哪一条记录出现了循环。需要在connect by中加上nocycle选项 

sys_connect_by_path 

按path的顺序把字段连起来,做行列转换的时候需要。 



Siblings 

用于对树形遍历结果的排序,普通的排序会打乱树形遍历结果的层次关系,加上此关键字,可以不改变树形遍历结果的层次关系,只是在每一层内部按关键字排序,相当于做组内排序了,用法为order siblings by col 



节点修剪和分支修剪 

where子句的限制将会做节点的修剪,但是其后代不会受到影响,相对应,connect by中加上条件,将会把满足条件的节点以及后代修剪,属于分支修剪。 

     

格式化输出 

利用lpad函数和level,可以格式化输出记录间父子层次关系,方便阅读。 



应用例子 



利用lpad函数和level伪列,格式化输出 

SQL> select empno,ename,lpad(empno,length(empno)+(level-1)*4,'-') from emp start with mgr is null connect by prior empno=mgr; 



EMPNO ENAME     LPAD(EMPNO,LENGTH(EMPNO)+(LEVE 

----- ---------- -------------------------------------------------------------------------------- 

7839 KING      7839 

7566 JONES     ----7566 

7788 SCOTT     --------7788 

7876 ADAMS     ------------7876 

7902 FORD      --------7902 

7369 SMITH     ------------7369 

7698 BLAKE     ----7698 

7499 ALLEN     --------7499 

7521 WARD      --------7521 

7654 MARTIN    --------7654 

7844 TURNER    --------7844 

7900 JAMES     --------7900 

7782 CLARK     ----7782 

7934 MILLER    --------7934 



自上向下,自下向上遍历查询 

自下向上遍历 

SQL> select empno,ename,level from emp start with empno=7788 connect by prior mgr=empno; 



EMPNO ENAME          LEVEL 

----- ---------- ---------- 

7788 SCOTT              1 

7566 JONES              2 

7839 KING               3 



自上向下遍历 

SQL> select empno,ename,level from emp start with empno=7788 connect by prior empno=mgr; 



EMPNO ENAME          LEVEL 

----- ---------- ---------- 

7788 SCOTT              1 

7876 ADAMS              2 



上面的sql语句等同于下面的sql 

SQL> select empno,ename,level from emp start with empno=7788 connect by mgr=prior empno; 



EMPNO ENAME          LEVEL 

----- ---------- ---------- 

7788 SCOTT              1 

7876 ADAMS              2 





节点修剪和分支修剪 



比如对scott 7788做节点修剪,只影响scoot,adams不受影响 

SQL> select empno,ename,lpad(empno,length(empno)+(level-1)*4,'-') from emp where empno<>7788 start with mgr is null connect by prior empno=mgr; 



EMPNO ENAME     LPAD(EMPNO,LENGTH(EMPNO)+(LEVE 

----- ---------- -------------------------------------------------------------------------------- 

7839 KING      7839 

7566 JONES     ----7566 

7876 ADAMS     ------------7876 

7902 FORD      --------7902 

7369 SMITH     ------------7369 

7698 BLAKE     ----7698 

7499 ALLEN     --------7499 

7521 WARD      --------7521 

7654 MARTIN    --------7654 

7844 TURNER    --------7844 

7900 JAMES     --------7900 

7782 CLARK     ----7782 

7934 MILLER    --------7934 



对比下面的分支修剪,可以看到2者的区别,下面的语句不但scott修剪掉了,而且其子孙节点adams也被修剪掉了 



SQL> select empno,ename,lpad(empno,length(empno)+(level-1)*4,'-') from emp start with mgr is null connect by prior empno=mgr and empno<>7788; 

EMPNO ENAME     LPAD(EMPNO,LENGTH(EMPNO)+(LEVE 

----- ---------- -------------------------------------------------------------------------------- 

7839 KING      7839 

7566 JONES     ----7566 

7902 FORD      --------7902 

7369 SMITH     ------------7369 

7698 BLAKE     ----7698 

7499 ALLEN     --------7499 

7521 WARD      --------7521 

7654 MARTIN    --------7654 

7844 TURNER    --------7844 

7900 JAMES     --------7900 

7782 CLARK     ----7782 

7934 MILLER    --------7934 



Where条件和connect by中的条件也可以混合使用 



查找父结点是7689且节点empno不是7521的所有纪录 

SQL> select empno,ename,lpad(empno,length(empno)+(level-1)*4,'-') from emp wherepriorempno= 7698 start with mgr is null connect by prior empno=mgr and empno<>7521; 



EMPNO ENAME     LPAD(EMPNO,LENGTH(EMPNO)+(LEVE 

----- ---------- -------------------------------------------------------------------------------- 

7499 ALLEN     --------7499 

7654 MARTIN    --------7654 

7844 TURNER    --------7844 

7900 JAMES     --------7900 





伪列Level,prior,connect_by_isleaf,connect_by_root的使用 

SQL> select empno,ename,level,prior empno p_empno,connect_by_isleaf isleaf,connect_by_root empno r_empno from emp start with mgr is null connect by prior empno=mgr ; 



EMPNO ENAME          LEVEL   P_EMPNO    ISLEAF   R_EMPNO 

----- ---------- ---------- ---------- ---------- ---------- 

7839 KING               1                    0      7839 

7566 JONES              2      7839         0      7839 

7788 SCOTT              3      7566         0      7839 

7876 ADAMS              4      7788         1      7839 

7902 FORD               3      7566         0      7839 

7369 SMITH              4      7902         1      7839 

7698 BLAKE              2      7839         0      7839 

7499 ALLEN              3      7698         1      7839 

7521 WARD               3      7698         1      7839 

7654 MARTIN             3      7698         1      7839 

7844 TURNER             3      7698         1      7839 

7900 JAMES              3      7698         1      7839 

7782 CLARK              2      7839         0      7839 

7934 MILLER             3      7782         1      7839 



伪列connect_by_iscycle, sys_connect_by_path的使用 



SQL> select empno,ename,connect_by_iscycle iscycle,sys_connect_by_path(empno,'/') path_empno from emp start with mgr is null connect by nocycle prior empno=mgr ; 



EMPNO ENAME        ISCYCLE PATH_EMPNO 

----- ---------- ---------- -------------------------------------------------------------------------------- 

7839 KING               0 /7839 

7566 JONES              0 /7839/7566 

7788 SCOTT              0 /7839/7566/7788 

7876 ADAMS              0 /7839/7566/7788/7876 

7902 FORD               0 /7839/7566/7902 

7369 SMITH              0 /7839/7566/7902/7369 

7698 BLAKE              0 /7839/7698 

7499 ALLEN              0 /7839/7698/7499 

7521 WARD               0 /7839/7698/7521 

7654 MARTIN             0 /7839/7698/7654 

7844 TURNER             0 /7839/7698/7844 

7900 JAMES              0 /7839/7698/7900 

7782 CLARK              0 /7839/7782 

7934 MILLER             0 /7839/7782/7934 





查看king下每一代的个数及平均sal 



SQL> select level,count(*),trunc(avg(sal)) from emp start with mgr is null connect by prior empno=mgr group by level; 



    LEVEL  COUNT(*) TRUNC(AVG(SAL)) 

---------- ---------- --------------- 

        1         1           5000 

        2         3           2758 

        4         2            950 

        3         8           1731 



确认节点之间是否存在层级关系,如存在,列出层级关系 

这可应用在产品的组装关系,员工的上下级领导关系的判断,如下面语句判断7839和7788是否有层级关系,如果返回结果,则表示存在层级关系 





SQL> select empno,ename,level,sys_connect_by_path(empno,'/') from emp where empno=7788 start with empno=7839 connect by mgr=prior empno; 



EMPNO ENAME          LEVEL SYS_CONNECT_BY_PATH(EMPNO,'/') 

----- ---------- ---------- -------------------------------------------------------------------------------- 

7788 SCOTT              3 /7839/7566/7788 



查找每个部分的老大,增加了限制条件prior deptno<> deptno 



SQL> select deptno,empno,ename,level from emp start with empno=7839 connect by mgr=prior empno and prior deptno<> deptno; 



DEPTNO EMPNO ENAME          LEVEL 

------ ----- ---------- ---------- 

   10 7839 KING               1 

   20 7566 JONES              2 

   30 7698 BLAKE              2 





树形遍历排序order siblings by 



先看一下用普通order取排序的情况 

SQL> select deptno,empno,ename,lpad(empno,length(empno)+(level-1)*4,'-') from emp start with mgr is null connect by prior empno=mgr order by ename; 



DEPTNO EMPNO ENAME     LPAD(EMPNO,LENGTH(EMPNO)+(LEVE 

------ ----- ---------- -------------------------------------------------------------------------------- 

   20 7876 ADAMS     ------------7876 

   30 7499 ALLEN     --------7499 

   30 7698 BLAKE     ----7698 

   10 7782 CLARK     ----7782 

   20 7902 FORD      --------7902 

   30 7900 JAMES     --------7900 

   20 7566 JONES     ----7566 

   10 7839 KING      7839 

   30 7654 MARTIN    --------7654 

   10 7934 MILLER    --------7934 

   20 7788 SCOTT     --------7788 

   20 7369 SMITH     ------------7369 

   30 7844 TURNER    --------7844 

   30 7521 WARD      --------7521 



原先层次关系已经被打乱了,order关键词后面加上silbings 



SQL> select deptno,empno,ename,lpad(empno,length(empno)+(level-1)*4,'-') from emp start with mgr is null connect by prior empno=mgr order siblings by ename; 



DEPTNO EMPNO ENAME     LPAD(EMPNO,LENGTH(EMPNO)+(LEVE 

------ ----- ---------- -------------------------------------------------------------------------------- 

   10 7839 KING      7839 

   30 7698 BLAKE     ----7698 

   30 7499 ALLEN     --------7499 

   30 7900 JAMES     --------7900 

   30 7654 MARTIN    --------7654 

   30 7844 TURNER    --------7844 

   30 7521 WARD      --------7521 

   10 7782 CLARK     ----7782 

   10 7934 MILLER    --------7934 

   20 7566 JONES     ----7566 

   20 7902 FORD      --------7902 

   20 7369 SMITH     ------------7369 

   20 7788 SCOTT     --------7788 

   20 7876 ADAMS     ------------7876 



层次关系没有变,每一层(level)内按ename排号序了

抱歉!评论已关闭.