ORACLE CONNECT BY 整理

本文介绍了在工作中遇到的一个关于Oracle的CONNECT BY查询优化问题。原有的实现方式使用游标遍历,导致执行效率低下。通过深入研究,发现了CONNECT_BY_ROOT函数可以替代游标,并整理了CONNECT BY的一些实用技巧,如PRIOR控制级联方向、NOCYCLE防止死循环以及LEVEL显示级联层级。文章还分享了一个利用CONNECT BY解决的有趣案例。

工作中一次 connect by 优化记录


有一个需求要查询所有下级级联关系,拿到的业务做的实现如下,

这里可能业务人员不知道CONNECT_BY_ROOT这个函数用游标遍历一遍,生产环境由于游标的基数过大,导致此存储一天都没有执行完成。

DECLARE
CURSOR CU_TEMP IS  SELECT * FROM SCOTT.EMP;
TYPE_EMP SCOTT.EMP%ROWTYPE;
V_SQL VARCHAR2(3000):='';
BEGIN
        OPEN CU_TEMP;
        LOOP  FETCH CU_TEMP INTO TYPE_EMP;
                EXIT WHEN CU_TEMP%NOTFOUND;
                V_SQL:='INSERT INTO T1(CROOT,EMPNO,MGR)'|| 'SELECT '''||TYPE_EMP.EMPNO||''',EMPNO,MGR FROM SCOTT.EMP START WITH EMPNO='||TYPE_EMP.EMPNO||' CONNECT BY PRIOR EMPNO =  MGR';
                DBMS_OUTPUT.PUT_LINE(V_SQL);
                EXECUTE IMMEDIATE V_SQL;
        END LOOP;
        CLOSE CU_TEMP;
END;
/


在研究存储过程后发现,这样做的唯一可参考的地方就是 在第一列展示根节点,但是这个可以用 CONNECT_BY_ROOT来代替~

于是整理了一下CONNECT BY 的一些用法

SELECT 
        SYS_CONNECT_BY_PATH(EMPNO,'/') C_PATH/*级联路径*/,
        CONNECT_BY_ROOT(EMPNO) C_ROOT/*获取根节点*/,
        CONNECT_BY_ISLEAF C_ISLEAF/*1表示最小子节点,0表示还存在子节点*/,
        CONNECT_BY_ISCYCLE C_CYCLE,/*循环级联到最后一级,此值为1*/
        LEVEL/*级联的层级*/,
        EMPNO,MGR
FROM TT
--WHERE EMPNO=7839 /*级联的范围,总记录结果集的范围*/
START WITH EMPNO=7839 /*确定开始位置*/
CONNECT BY NOCYCLE PRIOR EMPNO =  MGR /*PRIOR 会循环把这个变量赋值上一个级联的MGR的值,NOCYCLE 防止存在死循环的级联*/
ORDER BY LEVEL

自己觉得比较实用的一些用法

--PRIOR 此关键词的作用是 会向下替换MGR的值,控制级联方向。

--NOCYCLE 防止级联中的死循环

with t as
(
select 1 c1,2 c2 from dual
union all
select 2,3 from dual
union all
select 3,1 from dual
)
select c1,c2 from t connect by prior c1 = c2 start with c1=1

ORA-01436: 用户数据中的 CONNECT BY 循环

修改之后

with t as
(
select 1 c1,2 c2 from dual
union all
select 2,3 from dual
union all
select 3,1 from dual
)
select c1,c2,connect_by_ISCYCLE  from t connect by NOCYCLE  prior c1 = c2 start with c1=1

结果

c1   c2    CONNECT_BY_ISCYCLE

1 2 0
3 1 0
2 3 1


--LEVEL 第几级关联












网上发现的一些经典案例:

①字符‘12313’各位相加  1+2+3+1+3

SELECT SUM(C_SUBSTR) FROM
(
SELECT TO_NUMBER(SUBSTR('12313',LEVEL,1)) C_SUBSTR,LEVEL FROM DUAL CONNECT BY LEVEL <= LENGTH('12313')
)
②生成日期序列
SELECT TO_CHAR(SYSDATE-LEVEL+1,'YYYYMMDD') C_DATE FROM DUAL CONNECT BY LEVEL<=10


<think>嗯,用户这次询问的是Oracle数据库中的CONNECT BY语法及其使用示例。这个问题涉及到数据库中的层次查询功能,在Oracle中属于比较高级的SQL查询技巧。 用户应该正在学习Oracle的层次查询功能,可能是工作中遇到了需要处理树形结构数据的需求。从问题表述来看,用户对基础概念有一定了解,但需要更系统的说明和实用示例。 我注意到之前提供的引用资料虽然相关,但比较零散,需要整理成更系统的知识。特别是引用[1]中提到的WHERE子句与CONNECT BY条件的位置差异,以及引用[3]中关于start with和prior的用法说明,这些都是关键点。 在准备回答时,需要重点解释几个核心概念:start with子句定义查询起点,connect by子句建立父子关系,prior关键字指定递归方向,level伪列表示层级深度。还要强调nocycle和connect_by_isleaf这些实用功能,这在引用[2]中提到了。 对于示例部分,考虑设计一个简单的员工层级表,这样能清晰展示语法效果。包括员工ID、姓名和上级ID三个字段就足够演示了。示例查询要覆盖基本层次查询、查找叶子节点、路径展示等常见场景。 需要特别注意语法细节的准确性,比如prior的位置决定了查询方向是自顶向下还是自底向上。这个在引用[3]中也有提及,但要用更直白的语言说明。 最后生成的三个相关问题要围绕实际应用场景:性能优化、循环数据处理和实际应用案例,这样能帮助用户深化理解。特别是循环数据处理这点,在层次查询中是个常见痛点。</think>以下是Oracle中`CONNECT BY`语法的详细说明及使用示例: --- ### **一、语法说明** `CONNECT BY`用于处理**层次化查询**(树形结构数据),常与以下关键字组合: ```sql SELECT [列] FROM [表] WHERE [过滤条件] START WITH [根节点条件] -- 指定起始节点 CONNECT BY [父子关系条件] -- 定义层级关系 [ORDER SIBLINGS BY [排序列]]; -- 按兄弟节点排序 ``` - **`PRIOR`**: 表示父行引用。例如: - `PRIOR 子ID = 父ID` → **自顶向下**遍历(从根到叶) - `子ID = PRIOR 父ID` → **自底向上**遍历(从叶到根) - **`LEVEL`**: 伪列,表示当前节点在树中的**层级**(根节点为1,子节点递增)。 - **`CONNECT_BY_ROOT`**: 返回当前节点的**根节点**值。 - **`CONNECT_BY_ISLEAF`**: 标识当前节点是否为**叶子节点**(0:非叶子,1:叶子)。 - **`NOCYCLE`**: 避免循环引用导致的死锁(需配合`CONNECT_BY_ISCYCLE`检测循环)。 --- ### **二、使用示例** #### **示例表 `employees`** | EMPLOYEE_ID | NAME | MANAGER_ID | |-------------|-------|------------| | 101 | Alice | (null) | | 102 | Bob | 101 | | 103 | Carol | 101 | | 104 | Dave | 102 | #### **1. 自顶向下查询(从根到叶)** ```sql SELECT LEVEL AS 层级, LPAD(' ', 2*(LEVEL-1)) || NAME AS 员工姓名, -- 缩进显示层级 EMPLOYEE_ID, MANAGER_ID FROM employees START WITH MANAGER_ID IS NULL -- 从根节点(无上级)开始 CONNECT BY PRIOR EMPLOYEE_ID = MANAGER_ID; -- 父ID=当前行的上级ID ``` **结果**: | 层级 | 员工姓名 | EMPLOYEE_ID | MANAGER_ID | |------|----------|-------------|------------| | 1 | Alice | 101 | (null) | | 2 | Bob | 102 | 101 | | 3 | Dave | 104 | 102 | | 2 | Carol | 103 | 101 | #### **2. 自底向上查询(从叶到根)** ```sql SELECT LEVEL, NAME, CONNECT_BY_ROOT NAME AS 根节点 -- 显示当前分支的根节点 FROM employees START WITH EMPLOYEE_ID = 104 -- 从叶子节点开始 CONNECT BY PRIOR MANAGER_ID = EMPLOYEE_ID; -- 向上追溯 ``` **结果**: | LEVEL | NAME | 根节点 | |-------|------|--------| | 1 | Dave | Dave | | 2 | Bob | Dave | | 3 | Alice| Dave | #### **3. 检测叶子节点 & 避免循环** ```sql SELECT NAME, CONNECT_BY_ISLEAF AS 是否叶子节点 -- 叶子节点返回1 FROM employees WHERE CONNECT_BY_ISLEAF = 1 -- 仅显示叶子节点 START WITH MANAGER_ID IS NULL CONNECT BY NOCYCLE PRIOR EMPLOYEE_ID = MANAGER_ID; -- 避免循环引用 ``` **结果**: | NAME | 是否叶子节点 | |-------|--------------| | Dave | 1 | | Carol | 1 | --- ### **三、关键注意事项** 1. **条件位置影响结果**: - `WHERE` 子句在**连接前**过滤(影响所有行)。 - `CONNECT BY` 中的条件在**递归过程**中过滤(逐层影响)[^1]。 2. **排序控制**: `ORDER SIBLINGS BY` 保持兄弟节点的顺序(如按姓名排序同层级员工)。 3. **循环引用处理**: 使用 `NOCYCLE` + `CONNECT_BY_ISCYCLE` 可检测并跳过循环数据[^2]。 --- ### **四、典型应用场景** 1. 组织架构树查询 2. 分类层级展开(如商品类目) 3. 路径分析(如BOM物料清单) 4. 社交网络关系链追溯
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值