查看: 104728|回复: 166

[精华] 深入理解connect by【三年的坑,补完了】

[复制链接]
论坛徽章:
5
奥运会纪念徽章:摔跤
日期:2008-07-02 08:48:392009日食纪念
日期:2009-07-22 09:30:00优秀写手
日期:2013-12-18 09:29:11SQL数据库编程大师
日期:2016-01-13 10:30:43SQL大赛参与纪念
日期:2016-01-13 10:32:19
跳转到指定楼层
1#
发表于 2008-5-26 01:05 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 谁 于 2011-10-29 00:03 编辑

对于connect by,现在大多数人已经很熟悉了
connect by中的条件就表示了父子之间的连接关系
比如 connect by id=prior pid

但如果connect by中的条件没有表示记录之间的父子关系
那会出现什么情况?
常见的,connect by会在构造序列的时候使用
用select rownum from dual connect by rownum<xxx 代替早期版本的 select rownum from all_objects where rownum <xxx

我们注意到,dual是一个只有一条记录的表,如果表有多条记录,将会怎样?

下面开始实验
环境:windows xp sp2 + Oracle 9208
(10.1版本connect by有问题)

CREATE TABLE T
(
  ID  VARCHAR2(1 BYTE)
);

INSERT INTO T ( ID ) VALUES (
'A');
INSERT INTO T ( ID ) VALUES (
'B');
INSERT INTO T ( ID ) VALUES (
'C');
COMMIT;
  1. SQL> select id,level from t connect by level<2;

  2. I      LEVEL
  3. - ----------
  4. A          1
  5. B          1
  6. C          1

  7. SQL> select id,level from t connect by level<3;

  8. I      LEVEL
  9. - ----------
  10. A          1
  11. A          2
  12. B          2
  13. C          2
  14. B          1
  15. A          2
  16. B          2
  17. C          2
  18. C          1
  19. A          2
  20. B          2
  21. C          2

  22. 已选择12行。

  23. SQL> select id,level from t connect by level<4;

  24. I      LEVEL
  25. - ----------
  26. A          1
  27. A          2
  28. A          3
  29. B          3
  30. C          3
  31. B          2
  32. A          3
  33. B          3
  34. C          3
  35. C          2
  36. A          3
  37. B          3
  38. C          3
  39. B          1
  40. A          2
  41. A          3
  42. B          3
  43. C          3
  44. B          2
  45. A          3
  46. B          3
  47. C          3
  48. C          2
  49. A          3
  50. B          3
  51. C          3
  52. C          1
  53. A          2
  54. A          3
  55. B          3
  56. C          3
  57. B          2
  58. A          3
  59. B          3
  60. C          3
  61. C          2
  62. A          3
  63. B          3
  64. C          3

  65. 已选择39行。
复制代码
无需多说,我们很快可以找到其中的规律,假设表中有N条记录
则记F(N,l)为 select id,level from t connect by level<l 的结果集数目
那么,
F(N,1)=N
F(N,l) = F(N,l-1)*N+N

于是可以总结出
F(N,l)=∑power(N,p), p取值为[1,l)


要解释,也很容易
当连接条件不能限制记录之间的关系时
每一条记录都可以作为自己或者其他记录的叶子
如下所示:
A          1
A          2
A          3
B          3
C          3
B          2
A          3
B          3
C          3
C          2
A          3
B          3
C          3


在这里,我们看到的是
Oracle采用了深度优先的算法

论坛徽章:
5
奥运会纪念徽章:摔跤
日期:2008-07-02 08:48:392009日食纪念
日期:2009-07-22 09:30:00优秀写手
日期:2013-12-18 09:29:11SQL数据库编程大师
日期:2016-01-13 10:30:43SQL大赛参与纪念
日期:2016-01-13 10:32:19
2#
 楼主| 发表于 2008-5-26 01:05 | 只看该作者
本帖最后由 谁 于 2011-10-28 23:59 编辑

我们接着看一个例子,看看在SQL中通过connect by如何将任意一个整数(不要太大就行)拆分为若干个power(2,n)的和的方法。

先构造测试数据:
  1. create table ba(n number);
  2. insert into ba select 5*rownum from dual connect by rownum<5;
  3. commit;
  4. select * from ba;
复制代码
展示ba中的数据为:
N
-----------------------
5
10
15
20


一个得出结果的简单的SQL为
  1. select distinct a.n , level, bitand(a.n,power(2,level-1)) from ba a connect by level<=floor(log(2,n)+1)
复制代码
这里为什么要加distinct?你可以尝试去掉distinct ,看看结果与保持distinct有多大差别。

然后我们先来看,如果只对其中的一条记录进行操作,那么加不加distinct,结果是否是一样的?比如我们只看第一条记录5的拆分结果
  1. select distinct a.n , level, bitand(a.n,power(2,level-1)) from (select * from ba where rownum=1) a connect by level<=floor(log(2,n)+1);
复制代码
结果为:
  1. N    LEVEL    BITAND(A.N,POWER(2,LEVEL-1))
  2. ----------------------------------------------------------------
  3. 5    1             1
  4. 5    2             0
  5. 5    3             4
复制代码
去掉distinct的sql为
  1. select a.n , level, bitand(a.n,power(2,level-1)) from (select * from ba where rownum=1) a connect by level<=floor(log(2,n)+1);
复制代码
输出结果,自己运行一下看看。然后你就该思考了,为什么你看到的结果会是这样???
这里不做过多解释,做完上面的实验,然后结合1楼中所说的,我想你应该就能明白了。

———————————————————————我是Long Long Ago的大坑的分界线———————————————————————————

事实上我们有更好的办法来处理:
  1. with a as (select n, floor(log(2,n)+1) lc from ba)
  2. select a.n, bitand(a.n,power(2,b.rn-1)) from a,
  3. (select rownum rn from
  4.         (select max(lc) mlc from a)
  5.         connect by level<=mlc
  6. )b
  7. where rn<=a.lc
  8. order by 1,2
复制代码
内层SQL先取得所有记录中可拆分出来的power(2,n)中的n最大可能是多少,然后由此构造出序列,最后再做一次关联查询,用限制条件rn<=a.lc限制住每个N中可拆分出来的power(2,n)中的n的最大值,由此可以高效得出结果。

上例实质上与  对多记录按各自指定次数重复  的性质是一样的。


简单总结:
对单记录/单条数据使用connect by,没问题
但对多条记录使用connect by,就会碰到问题,千万要注意。

[ 本帖最后由 谁 于 2008-7-3 12:47 编辑 ]

使用道具 举报

回复
论坛徽章:
5
奥运会纪念徽章:摔跤
日期:2008-07-02 08:48:392009日食纪念
日期:2009-07-22 09:30:00优秀写手
日期:2013-12-18 09:29:11SQL数据库编程大师
日期:2016-01-13 10:30:43SQL大赛参与纪念
日期:2016-01-13 10:32:19
3#
 楼主| 发表于 2008-5-26 01:06 | 只看该作者
本帖最后由 谁 于 2011-10-28 23:24 编辑

再占一个
~~~~~~~~~~~~~~
再度登录之前看了一下自己的信息:
  • 在线时间211 小时
  • 注册时间2006-10-28 23:57
  • 最后访问2010-11-4 20:41
  • 上次活动时间2010-11-5 23:17
  • 上次发表时间2010-11-1 13:21
  • 所在时区(GMT +08:00) 北京, 香港, 帕斯, 新加坡, 台北

哇,快1年没登录了,不过我还是常常关注itpub,借此注册五周年之际,我将2楼的帖完成。
其实当时想说的就是当connect by的表中有多条记录时,如何使用connect by正确创造序列。但在没多久之后,其实已经有人给出了与我相同思路的做法,而在我要完成这个很长久很长久的坑的时候,发现已经有更强的帖子出现了——求反GROUP BY的写法。这个帖子聚集了各种各样的做法,向我们展示了要创造序列,不用connect by,也有很多办法可以做到,在此膜拜各位强人

使用道具 举报

回复
论坛徽章:
273
生肖徽章2007版:猪
日期:2008-09-27 09:35:45明尼苏达森林狼
日期:2009-01-12 14:15:09生肖徽章2007版:猪
日期:2009-01-21 16:30:59布鲁克林篮网
日期:2009-03-03 14:42:32圣安东尼奥马刺
日期:2009-03-03 14:44:41生肖徽章2007版:鸡
日期:2009-03-03 21:45:52生肖徽章2007版:牛
日期:2009-03-09 14:03:42生肖徽章2007版:猪
日期:2009-03-10 21:37:00生肖徽章2007版:羊
日期:2009-03-16 10:17:11生肖徽章2007版:虎
日期:2009-03-24 21:26:52
4#
发表于 2008-5-26 08:11 | 只看该作者
支持原创

使用道具 举报

回复
论坛徽章:
33
劳斯莱斯
日期:2013-08-08 14:01:23三菱
日期:2013-09-28 10:16:06一汽
日期:2013-11-19 17:01:11凯迪拉克
日期:2013-12-07 17:11:282014年新春福章
日期:2014-02-18 16:42:02马上有房
日期:2014-02-18 16:42:02itpub13周年纪念徽章
日期:2014-09-27 14:20:21itpub13周年纪念徽章
日期:2014-10-08 15:13:38懒羊羊
日期:2015-03-04 14:52:112015年新春福章
日期:2015-03-06 11:58:18
5#
发表于 2008-5-26 08:42 | 只看该作者
等待继续:LZ研究这个到深夜?

使用道具 举报

回复
论坛徽章:
4
数据库板块每日发贴之星
日期:2008-05-14 01:01:572010新春纪念徽章
日期:2010-03-01 11:19:50ITPUB 11周年纪念徽章
日期:2012-10-10 13:11:142013年新春福章
日期:2013-02-25 14:51:24
6#
发表于 2008-5-27 08:49 | 只看该作者
顶一个

使用道具 举报

回复
论坛徽章:
57
马上加薪
日期:2014-02-19 11:55:142011新春纪念徽章
日期:2011-01-25 15:42:562011新春纪念徽章
日期:2011-01-25 15:42:332011新春纪念徽章
日期:2011-01-25 15:42:152011新春纪念徽章
日期:2011-01-25 15:41:502011新春纪念徽章
日期:2011-01-25 15:41:01ITPUB9周年纪念徽章
日期:2010-10-08 09:28:522010系统架构师大会纪念
日期:2010-09-03 16:39:572010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010新春纪念徽章
日期:2010-03-01 11:21:02
7#
发表于 2008-5-27 09:40 | 只看该作者

没有楼主研究的深入!

使用道具 举报

回复
论坛徽章:
0
8#
发表于 2008-5-27 09:55 | 只看该作者
不错! 这两天我也困惑在这个问题上 也做了些测试 不过LZ的测试比我深入多了

关键是 connect by 后面不管是level 还是 rownum都是在查询结果集内作的限制
比方
Table:
X
--
1
2
3
那么select x from table connect by rownum <5
结果就是
x
---
1
1
1
1
2
3

使用道具 举报

回复
论坛徽章:
18
生肖徽章:鼠
日期:2006-09-07 17:01:082008新春纪念徽章
日期:2008-02-13 12:43:03生肖徽章:蛇
日期:2007-09-26 17:09:49生肖徽章:猪
日期:2007-09-26 17:09:04生肖徽章:马
日期:2007-09-26 17:03:24生肖徽章:龙
日期:2007-09-26 17:02:43生肖徽章:兔
日期:2007-09-26 17:02:19生肖徽章:羊
日期:2007-09-26 12:35:44生肖徽章:虎
日期:2007-09-18 15:23:56生肖徽章:狗
日期:2007-07-02 11:26:12
9#
发表于 2008-5-27 14:07 | 只看该作者
不错啊,楼主很有钻研精神啊。

使用道具 举报

回复
论坛徽章:
27
设计板块每日发贴之星
日期:2007-08-24 01:05:17奥运会纪念徽章:拳击
日期:2012-06-25 14:17:112012新春纪念徽章
日期:2012-01-04 11:49:54生肖徽章2007版:龙
日期:2009-04-07 18:18:35生肖徽章2007版:鸡
日期:2008-10-14 14:14:30生肖徽章2007版:龙
日期:2008-10-08 21:22:20铁扇公主
日期:2008-09-28 11:20:58授权会员
日期:2008-09-05 13:30:44ITPUB元老
日期:2008-09-05 13:30:31奥运会纪念徽章:摔跤
日期:2008-07-26 08:05:05
10#
发表于 2008-5-27 14:31 | 只看该作者
值得我学习,努力

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表