`
chorpin
  • 浏览: 130959 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

在数据库中对树进行遍历查询(转)

阅读更多
最近看到同事用到了这个功能,恰好也发现了一篇好文章,就转过来了。

在数据库中对树进行遍历查询,看看如何实现,这里的例子是用oracle来实现。

ORACLE Recursion query for TREE with "connect by/start with"    
-- Tirle        : Recursion query for TREE with "connect by/start with"
-- Author       : Rake Gao
-- Create Date  : 2005-08-22
-- Version      : 2.0
-- Last Modify  : 2005-08-22

  目  录
一、测试准备
二、实现各种查询要求
三、要点总结


  正  文
一、测试准备
1、先假设有如下部门结构。
       1
     /  \
    2    3
   /\    /|\
  4  5  6 7 8

2、然后建立测试表和数据。
drop table t_dept_temp;
create table t_dept_temp(
  DEPT_ID    NUMBER(2)    NOT NULL,
  PARENT_ID  NUMBER(2)    ,
  DEPT_NAME  VARCHAR2(10) ,
  AMOUNT     NUMBER(3)           --人数
);
delete t_dept_temp;
insert into t_dept_temp (DEPT_ID,PARENT_ID,DEPT_NAME,AMOUNT) values (1,null,'1'    ,2);
insert into t_dept_temp (DEPT_ID,PARENT_ID,DEPT_NAME,AMOUNT) values (2,1   ,'1-2'  ,15);
insert into t_dept_temp (DEPT_ID,PARENT_ID,DEPT_NAME,AMOUNT) values (3,1   ,'1-3'  ,8);
insert into t_dept_temp (DEPT_ID,PARENT_ID,DEPT_NAME,AMOUNT) values (4,2   ,'1-2-4',10);
insert into t_dept_temp (DEPT_ID,PARENT_ID,DEPT_NAME,AMOUNT) values (5,2   ,'1-2-5',9);
insert into t_dept_temp (DEPT_ID,PARENT_ID,DEPT_NAME,AMOUNT) values (6,3   ,'1-3-6',17);
insert into t_dept_temp (DEPT_ID,PARENT_ID,DEPT_NAME,AMOUNT) values (7,3   ,'1-3-7',5);
insert into t_dept_temp (DEPT_ID,PARENT_ID,DEPT_NAME,AMOUNT) values (8,3   ,'1-3-8',6);
commit;

SQL> select * from t_dept_temp;

DEPT_ID PARENT_ID DEPT_NAME  AMOUNT
------- --------- ---------- ------
      1           1               2
      2         1 1-2            15
      3         1 1-3             8
      4         2 1-2-4          10
      5         2 1-2-5           9
      6         3 1-3-6          17
      7         3 1-3-7           5
      8         3 1-3-8           6

3、调整一下输出格式
col DEPT_ID format A10;

二、接下来实现各种查询要求
1、部门2及其所有下级部门。
SELECT LPAD(' ',2*(LEVEL - 1), ' ')||DEPT_ID AS DEPT_ID,
       PARENT_ID,DEPT_NAME,AMOUNT
  FROM t_dept_temp
  CONNECT BY PARENT_ID = PRIOR DEPT_ID  -- 找出所有PARENT_ID等于当前记录DEPT_ID的记录。
  START WITH DEPT_ID = 2                -- 从部门2开始递归查询。
  ;
DEPT_ID    PARENT_ID DEPT_NAME  AMOUNT
---------- --------- ---------- ------
2                  1 1-2            15
  4                2 1-2-4          10
  5                2 1-2-5           9

2、部门4及其所有上级部门
SELECT LPAD(' ',2*(LEVEL - 1), ' ')||DEPT_ID AS DEPT_ID,
       PARENT_ID,DEPT_NAME,AMOUNT
  FROM T_DEPT_TEMP
  CONNECT BY PRIOR PARENT_ID = DEPT_ID  -- 找出所有DEPT_ID等于当前记录PARENT_ID的记录
  START WITH DEPT_ID = 4               -- 从部门4开始递归查询。
  ;
DEPT_ID    PARENT_ID DEPT_NAME  AMOUNT
---------- --------- ---------- ------
4                  2 1-2-4          10
  2                1 1-2            15
    1                1               2

3、部门1的所有下级部门。
SELECT LPAD(' ',2*(LEVEL - 1), ' ')||DEPT_ID AS DEPT_ID,
       PARENT_ID,DEPT_NAME,AMOUNT
  FROM T_DEPT_TEMP
  START WITH DEPT_ID = 1
  CONNECT BY PARENT_ID = PRIOR DEPT_ID;
DEPT_ID    PARENT_ID DEPT_NAME  AMOUNT
---------- --------- ---------- ------
1                    1               2
  2                1 1-2            15
    4              2 1-2-4          10
    5              2 1-2-5           9
  3                1 1-3             8
    6              3 1-3-6          17
    7              3 1-3-7           5
    8              3 1-3-8           6

4、部门1及其所有下级部门,但是不包括部门3及其下级部门。(排除树枝)
SELECT LPAD(' ',2*(LEVEL - 1), ' ')||DEPT_ID AS DEPT_ID,
       PARENT_ID,DEPT_NAME,AMOUNT
  FROM T_DEPT_TEMP
  START WITH DEPT_ID = 1
  CONNECT BY PARENT_ID = PRIOR DEPT_ID
         AND DEPT_ID <> 3    -- 不包括部门3及其下属部门(部门3和6、7、8都没出现)
  ;
DEPT_ID    PARENT_ID DEPT_NAME  AMOUNT
---------- --------- ---------- ------
1                    1               2
  2                1 1-2            15
    4              2 1-2-4          10
    5              2 1-2-5           9

5、部门1及其所有下级部门,但是仅不包括部门3。(排除节点)
SELECT LPAD(' ',2*(LEVEL - 1), ' ')||DEPT_ID AS DEPT_ID,
       PARENT_ID,DEPT_NAME,AMOUNT
  FROM T_DEPT_TEMP
  WHERE DEPT_ID <>3          -- 仅仅不包括部门3(输出结果中,3的下级部门6、7、8还是出现了)
  START WITH DEPT_ID = 1
  CONNECT BY PARENT_ID = PRIOR DEPT_ID  -- 执行顺序where在connect by之后
  ;
DEPT_ID    PARENT_ID DEPT_NAME  AMOUNT
---------- --------- ---------- ------
1                    1               2
  2                1 1-2            15
    4              2 1-2-4          10
    5              2 1-2-5           9
    6              3 1-3-6          17
    7              3 1-3-7           5
    8              3 1-3-8           6

6、部门1及其所有下级部门,且所有部门按照人数升序排列。
SELECT LPAD(' ',2*(LEVEL - 1), ' ')||DEPT_ID AS DEPT_ID,
       PARENT_ID,DEPT_NAME,AMOUNT
  FROM T_DEPT_TEMP
  START WITH DEPT_ID = 1
  CONNECT BY PARENT_ID = PRIOR DEPT_ID
  ORDER BY AMOUNT ASC -- 排序在最后被执行,所以DEPT_ID完全被打乱了,而且层级关系也打乱了。
  ;
-- In a hierarchical query, do not specify either ORDER BY or GROUP BY,
-- as they will destroy the hierarchical order of the CONNECT BY results.
DEPT_ID    PARENT_ID DEPT_NAME  AMOUNT
---------- --------- ---------- ------
1                    1               2
    7              3 1-3-7           5
    8              3 1-3-8           6
  3                1 1-3             8
    5              2 1-2-5           9
    4              2 1-2-4          10
  2                1 1-2            15
    6              3 1-3-6          17

7、部门1及其所有下级部门,每个部门的下一级部门之间,按照人数降序排列。(有同一上级的那些部门???
-- If you want to order rows of siblings of the same parent,
-- then use the ORDER SIBLINGS BY clause.
SELECT LPAD(' ',2*(LEVEL - 1), ' ')||DEPT_ID AS DEPT_ID,
       PARENT_ID,DEPT_NAME,AMOUNT
  FROM T_DEPT_TEMP
  START WITH DEPT_ID = 1
  CONNECT BY PARENT_ID = PRIOR DEPT_ID
  ORDER SIBLINGS BY AMOUNT ASC  -- 同属部门间排序
  ;
-- 输出结果可见,部门3、2作为一组进行排序,部门7、8、6一组,5、4一组。
DEPT_ID    PARENT_ID DEPT_NAME  AMOUNT
---------- --------- ---------- ------
1                    1               2
  3                1 1-3             8
    7              3 1-3-7           5
    8              3 1-3-8           6
    6              3 1-3-6          17
  2                1 1-2            15
    5              2 1-2-5           9
    4              2 1-2-4          10

三、要点总结
1、子句的语法书写顺序。
select -> from -> where -> start with -> connect by -> order by
where写在connect by后面就不行,报错。

2、子句的执行顺序
from -> start with -> connect by -> where -> select -> order by
执行顺序where在connect by之后,可以从例5证明。
可是书写SQL语句的时候,却只能写前面,注意理解。

3、如何理解和记忆“CONNECT BY PRIOR PARENT_ID = DEPT_ID ”的含义呢?
现在看这个例子似乎很直观,但是今后实际应用时,条件变化后,如何推断查询结果呢?
    这里我自己总结一种方法,前提是要理解SQL语句执行时,是一条一条记录来处理的。
每条满足START WITH语句条件的记录被依次取出,暂且把每次被取出处理的记录,称为当前记录。
“PRIOR PARENT_ID”表明从当前记录得到PARENT_ID,
然后" = DEPT_ID"说明找到表中所有DEPT_ID等于当前记录PARENT_ID的记录,也就是找当前记录PARENT_ID所指向的记录。
    因为PARENT_ID的取值含义是上级节点,所以说明是向树的根节点方向的搜索。(我的上级是谁?)
    反之,如果是“CONNECT BY PARENT_ID = PRIOR DEPT_ID”,“PRIOR”在DEPT_ID一边,就是找所有PARENT_ID等于当前记录DEPT_ID的记录,是向树的叶子方向的搜索。(谁的上级是我?)
    找到结果记录集以后,从第一条记录开始递归处理,依此类推。

4、前序遍历
由于是递归处理,从例3可以看出,树的根节点向叶子节点递归查询时,查询节点的顺序是按照树的前序遍历进行的。

5、排序
例6和例7说明了两种排序的区别。
In a hierarchical query, do not specify either ORDER BY or GROUP BY, as they will destroy the hierarchical order of the CONNECT BY results. If you want to order rows of siblings of the same parent, then use the ORDER SIBLINGS BY clause. See order_by_clause.

6、伪列LEVEL
只能随CONNECT BY子句一起使用,是一个整数,代表递归的层次深度。也就是节点在树中所处深度。
根节点时等于1,根节点的叶子节点的深度等于2,依此类推。
LPAD(' ',2*(LEVEL - 1), ' ')||DEPT_ID 正是利用了LEVEL来为每个层级的字段提供不同的缩进。

注意,写的时候要给遍历的那列起个别名。
0
0
分享到:
评论

相关推荐

    树和图在关系型数据库中的表示及遍历.doc

    树和图在关系型数据库中的表示及遍历

    TreeView (树视图)遍历数据库的方法

    TreeView (树视图)遍历数据库的方法

    数据结构树的遍历

    学习数据库可用手段,用了多种方法实现了树的非递归遍历。

    PHP 数据库树的遍历方法

    PHP数据库树的遍历方法

    在Mysql数据库里通过存储过程实现树形的遍历

    关于多级别菜单栏或者权限系统中部门上下级的树形遍历,oracle中有connect by来实现,mysql没有这样的便捷途径,所以MySQL遍历数据表是我们经常会遇到的头痛问题,下面通过存储过程来实现。 1,建立测试表和数据: ...

    C#父子关系树递归遍历方法(含源码).rar

    C#文档:二叉树、父子关系树(BOM常见存储形式)递归遍历取数并用树形结构显示方法;包含dbHelpSql类。复制代码运行DBConfig窗体链接数据库,表结构见“表结构.SQL”文档。

    预排序遍历树算法的无限级分类-存储过程实现

    如果你支持原创,请保留存储过程中对作者Taihom的文字注释和描述。 ---------------------------------------- MPTT分类算法的添加,修改,删除其实很容易,但是这个算法的排序就不是这么容易了。 我这里已经把分类...

    非递归遍历树图解.vsdx

    非递归遍历树图解,代码:https://blog.csdn.net/u012606924/article/details/91039044

    预排序遍历树算法牺牲写性能的改进

    结合数据结构与以及“预排序遍历树算法”, 利用关系数据库系统实现树型层次模型数据库的存储、检索、遍历、插入和删除等基本算法,并解决了“预排序遍历树算法”的一个缺点(牺牲写的性能)。

    VC 文件目录遍历生成树菜菜单.rar

    VC 文件目录遍历生成树菜菜单,生成目录树的VC 源码范例,自动读取指定文件夹下的所有目录和文件,并生成Tree目录树结构。PS注:示例程序读取的是“成绩表”文件夹下的目录和文件,因此在测试时候要把生成的exe从...

    论文研究-一种优化的空间连接算法.pdf

    空间数据库中空间连接操作是最重要、最耗时的操作之一,基于BFRJ算法研究了一种对中间连接索引优化排序的空间连接算法OBFRJ,该算法使用广度优先顺序对两棵R树进行同步遍历,对生成的中间连接索引采用了一种空间填充...

    DBTree示例,先序表示,一句sql显示整棵树

    这种数据结构的特点是:操作简单,几乎不用维护,然而优点带来的问题是对树进行遍历的时候系统开销极大,需要进行递归操作,因此不能够无限制的增加树的深度。普遍采用了异步读取的方式来减少系统开销。一些改进的...

    基于GPU的内存数据库索引技术研究

    通过对 CSB+-树的查询算法使用共享存储器的可行性分析,指出传统的缓存敏感技术的思想在复杂的 GPU 内存框架中并不适合使用。为验证提出的并行方案的有效性,在多个硬件平台上进行相关的实验论证。 5.在 GPU 平台上...

    树形结构结点编码表

    对存取在mysql数据库的树形结构应该有帮助。 对一棵树的结点进行编码的步骤如下: 首先,对根节点编码,调用TreeCodeSet.root()可以获得根结点编码。 然后,按自上而下,自左而右的顺序遍历树,调用TreeCodeSet....

    django-mptt:在django中实现修改的预遍历遍历树的实用程序

    该项目目前未维护django-mptt的替代... 以下是一些有关MPTT的文章,可以激发您的胃口,并提供有关该技术本身如何工作的详细信息:SQL中的树在数据库中存储分层数据在MySQL中管理分层数据 什么是django-mptt ? django-

    kohana-MPTT:实现MPTT的Kohana 3模块。 (修改的预排序树遍历)

    它提供了在数据库表中创建,移动和删除树节点的方法,并且具有一些实用程序方法。 文献资料 此页面上的文档也包含在guide文件夹中。 您可以使用Kohana的用户指南进行查看。 安装 使用MPTT之前,您需要激活和配置...

    element-ui树形控件后台返回的数据+生成组织树的工具类

    但是我错了,当时的数据库中只有不到10条的数据并且组织结构非常单一,随后同事导入了数据(6000多条),组织结构也不是如此单一的了,我在项目中固定了三层结构肯定是错的,要一个活的组织树。  网上有很多大牛写...

    C++ 数据库二叉树的实现

    2、分别调用先序、中序、后序遍历算法对前面建立好的二叉链表树进行遍历。要求分别显示遍历后的结点序列。(递归和非递归) 3、调用计算二叉树的结点总数、深度、叶子节点个数算法,统计上述二叉链表树的结点总数、...

Global site tag (gtag.js) - Google Analytics