一、 ORA-01436报错

开发遇到一个报错 ORA-01436: CONNECT BY loop in user data (ORA-01436: 用户数据中的 CONNECT BY 循环)。

1. 报错原因

根据网上的资料,产生这个错误的原因是数据形成了循环。例如下面这个语句:

SELECT r1.region_id,parent_id
FROM cnl_region r1
WHERE r1.region_id =1
START WITH r1.region_id = 1
CONNECT BY PRIOR r1.region_id = r1.parent_id;

如果数据在region_id为1的parent_id为24684,而region_id为24684的parent_id为1,这样就会产生了循环。

2. 解决办法

  • 使用 connect by nocycle,不推荐,该方法只是可以不报错,但很可能得不到正确的结果。
  • 修改基础数据

二、 层次查询CONNECT BY

connect by主要用于父子,祖孙,上下级等层级关系的查询。

1. 语法

有两种写法

CONNECT BY [ NOCYCLE ] condition [AND condition]... [ START WITH condition ]
-- 或者
START WITH condition CONNECT BY [ NOCYCLE ] condition [AND condition]...

解释:

  • start with:指定起始节点的条件
  • connect by:指定父子行的条件关系
  • prior:查询父行的限定符,格式: prior column1 = column2 或者 column1 = prior column2,这两种写法取得的数据刚好是相反的
  • level:伪列,表示层级,值越小层级越高,level=1为层级最高节点
  • nocycle:若数据表中存在循环行,那么不添加此关键字会报错,添加关键字后,便不会报错,但循环的两行只会显示其中的第一条。
  • connect_by_iscycle:在使用了nocycle之后才能使用此关键字,用于表示是否是循环行,0表示否,1表示是
  • connect_by_isleaf:是否是叶子节点,0表示否,1表示是

以下是一个简单的例子

create table employee(emp_id number(18),lead_id number(18),emp_name varchar2(200),salary number(10,2),dept_no varchar2(8)
);insert into employee values('1',0,'king','1000000.00','001');
insert into employee values('2',1,'jack','50500.00','002');
insert into employee values('3',1,'arise','60000.00','003');
insert into employee values('4',2,'scott','30000.00','002');
insert into employee values('5',2,'tiger','25000.00','002');
insert into employee values('6',3,'wudde','23000.00','003');
insert into employee values('7',3,'joker','21000.00','003');
commit;

2. connect by prior的用法

① connect by prior 子节点ID = 父节点ID,找出其所有子节点

记住这个结论:prior后是子节点id就找其所有子节点,是父节点id就找其所有祖先节点。

select emp_id,lead_id,emp_name,prior emp_name as lead_name,salary
from employee
start with  emp_id=1
connect by prior emp_id = lead_id
order by lead_id;

首先根据结论,因为是prior emp_id,所以该sql含义是找出emp_id=1职员的所有下属(子节点)

我们具体来看为什么:

prior指的是前一个值(最开始就是start with的条件查出的值),以上sql的执行步骤为:

  • start with emp_id=1 -> 谁的 lead_id=1,它们的emp_id是多少?(prior emp_id = lead_id)
  • 查出emp_id=2和3 -> 谁的 lead_id=2和3,它们的emp_id是多少 ?
  • 查出 emp_id=4,5,6,7 -> 谁的 lead_id=4,5,6,7,它们的emp_id是多少 ?
  • 发现没有了,查询结束

因此按照逻辑,该sql含义还是:找出emp_id=1职员的所有下属(子节点)

② connect by 子节点ID = prior 父节点ID,找出其所有祖先节点

select emp_id,lead_id,emp_name,prior emp_name as lead_name,salary
from employee
start with  emp_id=6
connect by emp_id = prior lead_id
order by lead_id;

这刚好跟上面相反,因为是prior lead_id,所以该sql含义是找出emp_id=6职员的所有上级(祖先节点)

prior指的是前一个值(最开始就是start with的条件查出的值):

  • emp_id=6的那位,你的leader是谁? -> 查出 lead_id=3,所以它leader是员工3
  • emp_id=3的那位 ,你的leader是谁?-> 查出 lead_id=1,所以它leader是员工1
  • emp_id=1的那位 ,你的leader是谁?-> 查出 lead_id=0
  • 因为实际上没有emp_id=0的,所以查询结束了

因此按照逻辑,该sql含义还是:找出emp_id=6职员的所有上级(祖先节点)

③ 查询一个节点的叔伯节点

with t as (
select employee.*,prior emp_name,level le
from employee
start with  emp_id=1
connect by prior emp_id = lead_id
)
select *
from t
left join t tt on tt.emp_id=6
where t.le = (tt.le-1)
and t.emp_id not in (tt.lead_id);

看起来不太好懂这样写条件什么意思,其实画一下就很好理解了

 条件 t tt   条件
emp_id lead_id level emp_id lead_id level
  1 0 1 1 0 1  
t.emp_id not in (tt.lead_id) 3 1 2 3 1 2  
t.le = (tt.le-1) 2 1 2 2 1 2  
  4 2 3 4 2 3  
  5 2 3 5 2 3  
  6 3 3 6 3 3 tt.emp_id=6
  7 3 3 7 3 3  

查询结果,就是emp_id=2的记录

④ 查询族兄

with t as (
select employee.*,prior emp_name,level le
from employee
start with emp_id=1
connect by prior emp_id=lead_id
)
select t.*
from t t
left join t tt on tt.emp_id=6
where t.le=tt.le and t.emp_id<>6;

这比刚才那个好理解,就是查跟自己同一等级的,t.emp_id<>6是为了避免查到自己

条件 t tt 条件
emp_id lead_id level emp_id lead_id level
  1 0 1 1 0 1  
  3 1 2 3 1 2  
2 1 2 2 1 2  
t.le = tt.le 4 2 3 4 2 3  
  5 2 3 5 2 3  
t.emp_id<>6 6 3 3 6 3 3 tt.emp_id=6
  7 3 3 7 3 3  

查询结果

⑤ level伪列的使用,格式化层级

select lpad(' ',level*2,' ')||emp_name as name,emp_id,lead_id,salary,levelfrom employeestart with emp_id=1connect by prior emp_id=lead_id;

⑥ connect_by_root 查找根节点

select connect_by_root emp_name,emp_name,lead_id,salaryfrom employee  start with lead_id=1connect by prior emp_id = lead_id;

⑦ connect_by_iscycle 标注循环行

connect_by_iscycle必须要与nocycle一起用,否则会报错。

-- 插入一条数据,与另一条emp_id=7的数据组成循环行
insert into employee values('3',7,'joker_cycle','21000.00','003');
commit;

此时有了循环行,如果不加nocycle,就会遇到最开始的 ORA-01436 错误。

select emp_id,emp_name,lead_id,salary
from employee
start with lead_id=0
connect by prior emp_id = lead_id;

添加nocycle后能查出,但结果不一定符合业务需求。

-- connect_by_iscycle("CYCLE"), connect by nocycle
select emp_id,emp_name,lead_id,salary,connect_by_iscycle as cycle
from employee
start with lead_id=0
connect by nocycle prior emp_id = lead_id;

⑧ connect_by_isleaf 判断是否为叶子节点

select emp_id,emp_name,lead_id,salary,connect_by_isleaf
from employee
start with lead_id=0
connect by nocycle prior emp_id=lead_id;

三、 高级用法

下面这些有点像算法题,有它的写法套路,了解了解,真遇到了照搬写法就行。

1. 范围展开

with tmp as
(select 2 as id1, 5 as id2 from dual
union all
select 8,10 from dual
) select id1,id2, id1+level-1 from tmp
connect by level<=id2-id1+1
and prior id1=id1
and prior dbms_random.value() is not null;

2. 拆分单个字符串

单字符串拆分成多行

var b1 varchar2(4000);
exec :b1:='100,101,102,103,104,105';select REGEXP_SUBSTR( :b1,'[^,]+', 1, level) as value
from dual
connect by level <= regexp_count(:b1, '[^,]+');

3. 拆分表字段字符串为多行

with tmp as
(select 1 as id,'c,b,a' as name from dual
union all
select 2,'d,e,f' from dual
union all
select 3,'h,g' from dual
)
select id, REGEXP_SUBSTR( name,'[^,]+', 1, level) as value
from tmp
connect by level <= regexp_count(name, '[^,]+')
and prior id=id
and prior dbms_random.value() is not null
order by id,value;

4. connect by与半连接

非常少有的in和exists性能不相等,建议in与connect by结合使用

参考

ORA-01436: 用户数据中的 CONNECT BY 循环_launch_225的博客-CSDN博客

oracle的start with connect by prior如何使用 - 李润 - 博客园

oracle之connect by的用法_W_DongQiang的博客-CSDN博客

tiger liu《SQL写法与改写-第二期》

ORA-01436 与 层次查询CONNECT BY相关推荐

  1. oracle 层次查询 connect by

    Hierarchical Queries 语法 connect by [nocycle] condition [start with condition] start with condition c ...

  2. PLSQL_基础系列11_递归和层次查询CONNECT BY(案例)

    2015-05-31 Created By BaoXinjian 一.摘要 1. 树结构的描述 树结构的数据存放在表中,数据之间的层次关系即父子关系,通过表中的列与列间的关系来描述,如EMP表中的EM ...

  3. oracle层次查询用处,Oracle描述层次查询(hierarchicalquery)

    欢迎进入Oracle社区论坛,与200万技术人员互动交流 >>进入 有关Oracle描述层次查询(hierarchical query)的详细情况,先看一张图: 正确答案:BD A错误,树 ...

  4. oracle层次查询中prior与自上而下、自下而上查询

    作为一名开发和管理兼修的工程师,有时候会遇到编写复杂的sql ,此时就会查找资料,事后又很快会忘记.记得自己每次编写层次结构的时候都会找资料,到底谁是父亲,谁是孩子,prior放在那边代表什么意思. ...

  5. mysql递归层次查询

    mysql递归层次查询 最近在做一个从oracle数据库到mysql数据库的移植,遇到一个这样的问题 在Oracle 中我们知道有一个 Hierarchical Queries 通过CONNECT B ...

  6. oracle 递归层次查询

    工作需要,要查询一个节点的所有子节点信息,在网上找了很久,没有看到直接的例子的,很多理论,所以给个例子,作为我这种小白水平的人看看: 举例子: create table SCAN_MERC_INFO ...

  7. Oracle 层次查询、递归

    Oracle 层次查询.递归 语法: select ... from tablename start with 条件1  connect by 条件2  where 条件3; 1. 树结构的描述  树 ...

  8. 剑破冰山之十一章 层次查询

    层次查询的函数的两种方式: SYS_CONNECT_BY_PATH WMSYS.WM_CONCAT() 语法: SELECT-START WITH-CONNECT BY PRIOR X1=X2 练习数 ...

  9. oracle sql查询缺失号,Oracle层次查询和分析函数

    摘要 一组连续的数,去掉中间一些数,如何求出剩下的数的区间(即号段)?知道号段的起止,如何求出该号段内所有的数?知道一个大的号段范围和已经取过的号段,如何求出可用的号段?利用Oracle提供的强大的查 ...

最新文章

  1. cassandra mysql_cassandra命令行操作
  2. Android混淆代码
  3. php drive mssql,PHP 连接 MSSQL 2005/2008 以UTF8存取 并让ADODB支持的安装设置
  4. 【Matlab】到底怎么自定义colorbar/colormap的颜色?
  5. kernel shell bash简介
  6. python3 文件相关操作
  7. c++------------之---【虚函数和抽象基类的应用】
  8. [20130706]传说中的中断风暴
  9. Graph Embedding图嵌入
  10. cssd oracle,Oracle RAC /etc/init.d/init.cssd startcheck
  11. AndroidStudio 内存泄漏分析 Memory Monitor
  12. neo4j︱Cypher完整案例csv导入、关系联通、高级查询(三)
  13. 构建大数据网络 你不得不重视的六个问题
  14. 微信支付基于图计算的反欺诈实践
  15. 完全体,千字详解:“Java性能调优六大工具”之JConsole工具
  16. nginx错误502,503,504分析
  17. CBA公布对北京首钢处罚结果 相关工作人员遭重罚
  18. hadoop cgroup源码解读
  19. 容联云通讯发送短信java实现
  20. 矩阵切换器有哪些控制方式,有什么好处

热门文章

  1. 2017年4月24号课堂笔记
  2. php安全新闻早八点-Microdoor-第三季
  3. 项目专题 1: 结构化方法学自动取款机系统(ATM)分析
  4. 新注册公众号没有留言评论功能怎么办?如何开通公众号留言功能?
  5. QT获取微秒级时间戳
  6. python统计有几个单词_统计文件中单词的个数---Shell及python版
  7. Box2D 源码编译
  8. 李航《统计学习方法》学习日记【1】
  9. 形式逻辑(08)模态判断 和 推理
  10. 环境化学试题及标准答案