`
yanfaguanli
  • 浏览: 659894 次
文章分类
社区版块
存档分类
最新评论

ORACLE函数之connect by

 
阅读更多
目 录

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


正 文
一、测试准备
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来为每个层级的字段提供不同的缩进。

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


正 文
一、测试准备
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来为每个层级的字段提供不同的缩进。

分享到:
评论

相关推荐

    oracle常用函数.rar

    oracle 常用函数,connect by用法,分页方法

    oracle connect by 和 分析函数总结.doc

    oracle connect by 和 分析函数总结.doc

    oracle sys_connect_by_path 函数 结果集连接

    几天和几个网上朋友一起探讨oracle开发中的一些特别之处,谈到了竖横对换的方式。

    oracle函数介绍(8) 综述.doc

     单值函数多数处理单个或多个值但只返回单个值(SYS_CONNECT_BY_PATH除外)。  聚合函数多数处理多行并且各分组序列分别返回成一行。  分析函数多数处理多行并且每条记录均会有返回。  需要注意不同类型函数可...

    MySQL多种递归查询方法.docx

    Oracle 递归查询, start with connect by prior 用法 find_in_set 函数 concat,concat_ws,group_concat 函数 MySQL 自定义函数 手动实现 MySQL 递归查询 Oracle 递归查询 在 Oracle 中是通过 start ...

    oracle分割字符串方法

    这里利用了函数 regexp_substr和connect by 及oracle的正则相关函数实现oracle分割字符串方法

    oracle多行转为字符串总结

    介绍了将多行转为字符串的三种方案,并比较了三种方案的执行效率. 1.sys_connect_by_path + start with ... connect by ... prior + 分析函数 2.自定义Function/SP 3.使用 Oracle 10g 内置函数 wmsys.wm_concat

    oracle列合并的实现方法

    很多场合我们都会用到...sys_connect_by_path(字段名, 2个字段之间的连接符号),这里的连接符号不要使用逗号,oracle会报错,如果一定要用,可以使用replace替换一下,方法如下 REPLACE(字段名,原字符,’,’)。这个

    最全的oracle常用命令大全.txt

    ORACLE的数据字典是数据库的重要组成部分之一,它随着数据库的产生而产生, 随着数据库的变化而变化, 体现为sys用户下的一些表和视图。数据字典名称是大写的英文字符。 数据字典里存有用户信息、用户的权限信息、...

    Oracle SQL高级编程(资深Oracle专家力作,OakTable团队推荐)--随书源代码

    10.4.2 SYS_CONNECT_BY_PATH函数 279 10.4.3 CONNECT_BY_ROOT运算符 281 10.4.4 CONNECT_BY_ISCYCLE伪列和NOCYCLE参数 284 10.4.5 CONNECT_BY_ISLEAF伪列 287 10.5 小结 291 第11章 半联结和反联结 292 11.1 ...

    ORACLE常用数值函数、转换函数、字符串函数

    单值函数在查询中返回单个值,可被应用到select,where子句,start with以及connect by 子句和having子句。 (一).数值型函数(Number Functions) 数值型函数输入数字型参数并返回数值型的值。多数该类函数的返回值...

    韩顺平oracle学习笔记

    oracle的函数; oracle数据库管理;oracle 的权角色; pl/sql 编程; 索引,约束和事物。 期望目标: 1 学会安装、启动、卸载oracle 2 使用sql *plus工具 3 掌握oracle用户管理 4 学会在oracle中编写简单的select...

    oracle sql 行列转换

    行列转换,sys_connect_by_path,row_number等函数的用法

    oracle学习文档 笔记 全面 深刻 详细 通俗易懂 doc word格式 清晰 连接字符串

    ORACLE数据库系统是美国ORACLE公司(甲骨文)提供的以分布式数据库为核心的一组软件产品,是目前最流行的客户/服务器(CLIENT/SERVER)或B/S体系结构的数据库之一。  拉里•埃里森  就业前景 从就业与择业的...

    Oracle常用查询操作大全

    Oracle常用的sql语句文件以及说明(doc、txt)文件 例如:analyze分析表、oracle 常用表和视图、Select Start with ... Connect by 分层次输出、SQL语句优化、函数的使用和说明。

    Oracle8i_9i数据库基础

    第一部分 Oracle SQL*PLUS基础 23 第一章 Oracle数据库基础 23 §1.1 理解关系数据库系统(RDBMS) 23 §1.1.1 关系模型 23 §1.1.2 Codd十二法则 24 §1.2 关系数据库系统(RDBMS)的组成 24 §1.2.1 RDBMS 内核 24...

    oracle统计时间段内每一天的数据(推荐)

    CONNECT BY ROWNUM &lt;= 1000; 注:DBMS_RANDOM.VALUE(A, B)是随机数产生函数, A是区间的开始,B是区间的结束 2. 例如:拆分以逗号隔开的 A,B,C,D 字符串 SELECT REGEXP_SUBSTR('A,B,C,D', '[^,]+', 1, ROWNUM) ...

    Oracle 两个逗号分割的字符串,获取交集、差集(sql实现过程解析)

    Oracle数据库的两个字段值为逗号分割的字符串,例如:字段A值为“1,2,3,5”,字段B为“2”。...connect by rownum &lt;= length(regexp_replace(id, '[^,]+')) +1 intersect -- 取交集 select regexp_substr

Global site tag (gtag.js) - Google Analytics