connect by rownum及connect by level的内部执行原理及过程
2014-06-13 20:30
507 查看
来自兔子大神blog地址:http://www.itpub.net/thread-1570306-1-1.html
SQL> with tmp as (select 2 a from dual union all select 3 from dual union all select 4 from dual)
2 select rownum, b.*
3 from (select a.a, sum(a.a) over(order by rownum) - rownum + 1 psum
4 from tmp a) b
5 connect by rownum <= b.psum;
ROWNUM A PSUM
---------- ---------- ----------
1 2 2
2 2 2
3 3 4
4 3 4
5 4 7
6 4 7
7 4 7
8 3 4
9 4 7
9 rows selected
以上红色部分,实在搞不懂为什么会出现在结果集中。
因为按connect by rownum <= psum的条件,8、9都是大于4、7的。但是却出现在了结果集中,不明白这是何原因。
有谁可以解释一下?
您只给出了内层的查询,我不明白这是什么意思......
我能明白这个内层查询的含义,但是不能理解外层查询中rownum=8/9的情况下,为何会出现psum=4/7的情况。
因为此时rownum(8/9) > psum(4/7)了,为何会在connect by rownum <= psum的条件下出现这样的结果?
虽然结果是我想要的,但是不明白这是为什么。
实际上,我是想知道,为何数据会是这样展现的?他的过程是怎么样的?
对于结果:
a b
2 2
3 4
4 7
可以推算,
a=2时,rownum <= 2(b的值),此时rownum=1,则有:
rownum a b
1 2 2
2 2 2--直到rownum=2
a=3时,rownum <= 4,此时rownum=3,则有:
rownum a b
3 3 4
4 3 4--直到rownum=4
a=3时,rownum <= 7,此时rownum=5,则有:
rownum a b
5 4 7
6 4 7
7 4 7--直到rownum=7
于是经过connect by rownum <= psum就可以得到:
1 2 2
2 2 2
3 3 4
4 3 4
5 4 7
6 4 7
7 4 7
8 3 4
9 4 7
但是接下来红色部分的8、9是如何推算的?
rownum a b
8 3 4
9 4 7
还是我这样推算是错误的?
原帖由 CaptainKobe 于 2009-12-15 16:32 发表
SQL> SELECT ROWNUM FROM DUAL CONNECT BY ROWNUM
你这个是静态的,而且是单条的情况下。
考虑多条,多个rownum限定值时,情况就没有那么简单了。
至于你的rownum<=10为什么有11条,我就不得而知了。应该是10条:
SQL> select rownum from dual connect by rownum <= 10 ;
ROWNUM
----------
1
2
3
4
5
6
7
8
9
10
10 rows selected
总结了下规律,发现
不管rownum如何限制,他首先会根据指定的psum值生成若干条数据,直到rownum=这个指定的值时停下,然后转入下一个psum。如rownum=3,而psum=4时,会生成rownum=3和4两条记录。
因此生成的数据为psum-开始时的rownum+1,比如上面的4-3+1=2。
但是,oracle在最后还会根据每个psum自动生成一行,也就是将原始表再复制一次(第一条不复制,不管psum是否有重复都会复制)。
于是生成的记录变成了:4-3+1+1=3了。就是我们所要的行数。
加上LEVEL就明白了:
with tmp as (select 2 a from dual union all select 3 from dual union all select 4 from dual)
select rownum,level, b.*
from (select a.a, sum(a.a) over(order by rownum) - rownum + 1 psum
from tmp a) b
connect by rownum <= b.psum;
ROWNUM LEVEL A PSUM
---------- ---------- ---------- ----------
1 1 2 2
2 2 2 2
3 3 3 4
4 4 3 4
5 5 4 7
6 6 4 7
7 7 4 7
8 1 3 4
9 1 4 7
LEVEL 1是不受CONNECT BY条件限制的。
因为你没有指定START WITH, 所以三行全部都是起点,即结果中你看到的三个LEVEL 1的行。这三行是一定会出来的,不受你CONNECT BY限制。
从2,2起点的结果从LEVEL 1一直到LEVEL 7
其他两行的结果就是你看到的8, 9行,它们没有更高的LEVEL因为从LEVEL 2开始已经不满足CONNECT BY条件,遍历就终止了。
我大概看明白你的意思了。
2 2
3 4
4 7
是不是可以这样理解,我开始查询的时候,rownum=1。然后由于connect by rownum <= 2,于是生成两条记录。
即:
rownum a b
1 2 2
2 2 2
然后,此时rownum=2,rownum再与4比较,发现比4小。于是再生成记录,直到=4为止,即:
rownum a b
1 2 2
2 2 2
3 3 4
4 3 4
然后,此时rownum=4,再与7比较,发现比7小。于是再生成记录,直到=7为止,即:
rownum a b
1 2 2
2 2 2
3 3 4
4 3 4
5 4 7
6 4 7
7 4 7
最后发现第一轮已经轮询完了,然后转入第二轮,由于没有指定start with,
所以接下来的数据都只生成一条,其他的rownum条件都已经不满足rownum<=psum的条件了
理解正确。
CONNECT BY一般是要写前后关系的,即 PRIOR 列名1 = 列名2
没有这个关系的用法经常是在表中只有一行的情况,(比如DUEL),然后用ROWNUM或LEVEL来控制输出的行数。
对于有N条记录的来说,如果没有递归条件,直接connect by level,先深度搜索,再广度,则 每个节点作为根节点,然后自身和其他节点为子节点,然后下个子节点还包括自身和其他节点,然后同样迭代 所以,总共记录数有N*2^0+N*2^1+......... 其中0,1....为level 则记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) 总记录数N,level层数P 结果集数:T=∑N^x(x=1...p) 比如,总记录数为3,层数为3 则结果集数:3^1 +3^2 + 3^3 = 3+9+27=39 |
SELECT rowid,a,b,level,rownum FROM test1 CONNECT BY level<=3; 对于记录数N,则第m层记录数为N^m N=3,LEVEL<=1 s=N LEVEL<=2 s=N^1+N^2 LEVEL<=3 s=N^1+N^2+N^3 ... LEVEL<=m s=N^1+N^2+N^3+....N^m 等比数列q=N,a1=N s=N(1-N^m)/(1-N) 其中m为需要迭代的level最大值 dingjun123@ORADB> select * from t; ID ---------- 1 1 已选择2行。 已用时间: 00: 00: 00.04 dingjun123@ORADB> select rownum from t connect by rownum <= 2; ROWNUM ---------- 1 2 3 已选择3行。 已用时间: 00: 00: 00.01 connect by 的本质分析 执行下面两句: select level,rownum from dual connect by level<=10; select level,rownum from dual connect by rownum<=10; 两句显示的效果完全一样, 但是,如果select有2条或更多,效果就大不一样 select A.TABLE_NAME,level,rownum from (select * from USER_TABLES A WHERE ROWNUM<=2 ) A connect by level<=10; --有2046条 select A.TABLE_NAME, level,rownum from (select * from USER_TABLES WHERE ROWNUM<=2) A connect by rownum<=10 ; --有11条; 所以,想咨询专家 connect by level 和 connect by rownum 的本质区别; 列个关系列表,也许可以通过数学归纳法,得出其中规律 select 的记录数(m) connect by 层数(n) by level 的记录数 by rownum的记录数 1 10 10 10 2 10 2046 11 3 10 88572 12 4 10 1398100 13 by rownum的记录数 规律很明显: connect by 层数(n) +select 的记录数(m) -1; by level 的记录数 的规律还没找出 --深刻理解connect by的原理,理解statr with,connect by,理解层次如何生成的 --理解connect by level和connect by rownum的区别,比如掌握level如何生成,rownum如何生成 --connect by level是先做递归,然后判断level是否满足条件,如果有多行,则每行为根,没有其他条件则 --子节点是自身的子,其他节点也是自身的子。。。循环,因此,connect by level<0也是有结果的,相当于没有写 --connect by level<0是先做递归,先做1次递归,发现level=1,不满足条件,之后结束,并不是丢弃行(和where不同),根总会递归 --但是如果有下属的,不满足条件的,则会丢弃,详细见后面的例子 --connect by rownum,没有其他条件,是直接递归迭代第一行(反复),然后判断rownum条件,不满足条件结束。正是因为 --递归如果没有start with则每行都是根,先第一轮递归,递归到rownum<n不满足后结束,然后其他根肯定不满足,只选出自身一行。 --则剩余其他几行都显示一次,因为第1行递归结束后,继续其他行,发现都不满足,结束。这个rownum不是select里的rownum --connect by 1=1就不结束了 --没有start with,没有prior...的迭代,不管是level,还是rownum,每行都是根节点,自身或其它节点是子节点 --区别是connect by level会先深度搜索,也就是根---自身---其他节点 --connect by rownum则是不停迭代自身(第一行),然后判断rownum。。。。 ---有3行 --它只对第一行做循环,到ROWNUM=4的时候停止。然后另外两条叠加上去,这是因为没有START WITH, 所以第一层所有行都会选中。 如果是先把第一层数据拿出来,再循环,那么结果就不是这样了。 CONNECT BY里面最好用LEVEL控制,ROWNUM的生成顺序不是我们能控制的。 dingjun123@ORADB> SELECT rowid,a,b,level,rownum FROM test1 CONNECT BY rownum<7; --迭代第一行,到第rownum=6结束,其他2行直接显示,select rownum不一样 ROWID A B LEVEL ROWNUM ------------------ ---------- ---------- ---------- ---------- AAAVu6AAEAAAsF2AAA 1 1 1 1 --第1到第6反复递归第一行 AAAVu6AAEAAAsF2AAA 1 1 2 2 AAAVu6AAEAAAsF2AAA 1 1 3 3 AAAVu6AAEAAAsF2AAA 1 1 4 4 AAAVu6AAEAAAsF2AAA 1 1 5 5 AAAVu6AAEAAAsF2AAA 1 1 6 6 AAAVu6AAEAAAsF2AAB 1 1 7 --不满足条件,递归结束 AAAVu6AAEAAAsF2AAC 1 2 1 8 --同上 已选择8行。 已用时间: 00: 00: 00.01 dingjun123@ORADB> SELECT rowid,a,b,level,rownum FROM test1 CONNECT BY rownum<-1; --不满足条件,相当于没有写 ROWID A B LEVEL ROWNUM ------------------ ---------- ---------- ---------- ---------- AAAVu6AAEAAAsF2AAA 1 1 1 1 AAAVu6AAEAAAsF2AAB 1 1 2 AAAVu6AAEAAAsF2AAC 1 2 1 3 已选择3行。 已用时间: 00: 00: 00.01 dingjun123@ORADB> SELECT rowid,a,b,level,rownum FROM test1 CONNECT BY rownum<0; --同上 ROWID A B LEVEL ROWNUM ------------------ ---------- ---------- ---------- ---------- AAAVu6AAEAAAsF2AAA 1 1 1 1 AAAVu6AAEAAAsF2AAB 1 1 2 AAAVu6AAEAAAsF2AAC 1 2 1 3 已选择3行。 已用时间: 00: 00: 00.01 dingjun123@ORADB> SELECT rowid,a,b,level,rownum FROM test1 CONNECT BY 1=0; --同上 ROWID A B LEVEL ROWNUM ------------------ ---------- ---------- ---------- ---------- AAAVu6AAEAAAsF2AAA 1 1 1 1 AAAVu6AAEAAAsF2AAB 1 1 2 AAAVu6AAEAAAsF2AAC 1 2 1 3 已选择3行。 已用时间: 00: 00: 00.01 dingjun123@ORADB> SELECT rowid,a,b,level,rownum FROM test1 CONNECT BY level<0; --同上 ROWID A B LEVEL ROWNUM ------------------ ---------- ---------- ---------- ---------- AAAVu6AAEAAAsF2AAA 1 1 1 1 AAAVu6AAEAAAsF2AAB 1 1 2 AAAVu6AAEAAAsF2AAC 1 2 1 3 已选择3行。 dingjun123@ORADB> SELECT rowid,a,b,level,rownum FROM test1 CONNECT BY level<3; --每行为根迭代,自身和其他行是子节点 ROWID A B LEVEL ROWNUM ------------------ ---------- ---------- ---------- ---------- AAAVu6AAEAAAsF2AAA 1 1 1 1 --根,其他包括自身的3行是子 AAAVu6AAEAAAsF2AAA 1 1 2 2 AAAVu6AAEAAAsF2AAB 1 2 3 AAAVu6AAEAAAsF2AAC 1 2 2 4 AAAVu6AAEAAAsF2AAB 1 1 5 AAAVu6AAEAAAsF2AAA 1 1 2 6 AAAVu6AAEAAAsF2AAB 1 2 7 AAAVu6AAEAAAsF2AAC 1 2 2 8 AAAVu6AAEAAAsF2AAC 1 2 1 9 AAAVu6AAEAAAsF2AAA 1 1 2 10 AAAVu6AAEAAAsF2AAB 1 2 11 AAAVu6AAEAAAsF2AAC 1 2 2 12 已选择12行。 SELECT rowid,a,b,level,rownum FROM test1 CONNECT BY level<4; --3条记录有39行 ROWID A B LEVEL ROWNUM AAAVu6AAEAAAsF0AAA 1 1 1 1 ---- AAAVu6AAEAAAsF0AAA 1 1 2 2 AAAVu6AAEAAAsF0AAA 1 1 3 3 第一行到第5行是level=2,第1行自身level=1,level=2 AAAVu6AAEAAAsF0AAB 1 3 4 对于level=3的又从自身开始,N条记录 AAAVu6AAEAAAsF0AAC 1 2 3 5 总数目等比数列和a1(1-q^n)/(1-q)=N(1-N^level)/(1-N) =3(1-3^3)(1-3) AAAVu6AAEAAAsF0AAB 1 2 6 ---- AAAVu6AAEAAAsF0AAA 1 1 3 7 AAAVu6AAEAAAsF0AAB 1 3 8 AAAVu6AAEAAAsF0AAC 1 2 3 9 AAAVu6AAEAAAsF0AAC 1 2 2 10 AAAVu6AAEAAAsF0AAA 1 1 3 11 AAAVu6AAEAAAsF0AAB 1 3 12 AAAVu6AAEAAAsF0AAC 1 2 3 13 AAAVu6AAEAAAsF0AAB 1 1 14 AAAVu6AAEAAAsF0AAA 1 1 2 15 AAAVu6AAEAAAsF0AAA 1 1 3 16 AAAVu6AAEAAAsF0AAB 1 3 17 AAAVu6AAEAAAsF0AAC 1 2 3 18 AAAVu6AAEAAAsF0AAB 1 2 19 AAAVu6AAEAAAsF0AAA 1 1 3 20 AAAVu6AAEAAAsF0AAB 1 3 21 AAAVu6AAEAAAsF0AAC 1 2 3 22 AAAVu6AAEAAAsF0AAC 1 2 2 23 AAAVu6AAEAAAsF0AAA 1 1 3 24 AAAVu6AAEAAAsF0AAB 1 3 25 AAAVu6AAEAAAsF0AAC 1 2 3 26 AAAVu6AAEAAAsF0AAC 1 2 1 27 AAAVu6AAEAAAsF0AAA 1 1 2 28 AAAVu6AAEAAAsF0AAA 1 1 3 29 AAAVu6AAEAAAsF0AAB 1 3 30 AAAVu6AAEAAAsF0AAC 1 2 3 31 AAAVu6AAEAAAsF0AAB 1 2 32 AAAVu6AAEAAAsF0AAA 1 1 3 33 AAAVu6AAEAAAsF0AAB 1 3 34 AAAVu6AAEAAAsF0AAC 1 2 3 35 AAAVu6AAEAAAsF0AAC 1 2 2 36 AAAVu6AAEAAAsF0AAA 1 1 3 37 AAAVu6AAEAAAsF0AAB 1 3 38 AAAVu6AAEAAAsF0AAC 1 2 3 39 ----------------例子--------------- DROP TABLE tt; CREATE TABLE tt(ID number,NAME VARCHAR2(10)); INSERT INTO tt VALUES(1,'aa'); INSERT INTO tt VALUES(2,'bb'); INSERT INTO tt VALUES(3,'cc'); COMMIT; 加个level就明白了,connect by level递归,无其他条件,就是每行都是根,然后自身和其他行是子孙,反复迭代 SQL> select ID,NAME,LEVEL from tt connect by level < 3; ID NAME LEVEL ---------- ---------- ---------- 1 aa 1 --根 1 aa 2 2 bb 2 3 cc 2 --包括自身的3个子孙,满足level<3条件结束 2 bb 1 1 aa 2 2 bb 2 3 cc 2 3 cc 1 1 aa 2 2 bb 2 3 cc 2 12 rows selected 也就是对于记录数为N的,如果level最大为m,那么相应的层次c上面的记录数是N^c,因此所有的记录数就是N+N^2+N^3+....N^m=N(1-N^m)/(1-m), 比如3条记录数的,当level<4的话,m=3,则总记录数为39条。找个规律就行了,也就是先深度搜索 同样,下面的也是每行为根,不同的是只搜索level<id 注意一点,根总会递归出来的(不会丢弃),但是下属子节点会判断不满足丢弃行, 比如第一行,虽然id=1,不满足level<id,但是也会出现,和下面的类似 select * from dual connect by level <0;返回一行结果 SQL> select ID,NAME,LEVEL from tt connect by LEVEL < ID; ID NAME LEVEL ---------- ---------- ---------- 1 aa 1 3 cc 2 --bb的id=2不满足条件 2 bb 1 3 cc 2 3 cc 1 3 cc 2 6 rows selected --因为level<=1或level<0都会返回一条数据 select rnum from (select level rnum from dual connect by level <= 2 - 1) where 2 <> 1 / 竟然返回 RNUM --------- 1 with t as (select 1 id from dual union all select 2 from dual) select id,level from t connect by level<=10 order by id,level; select 2*(1-power(2,10))/-1 from dual; q≠1时 Sn=a1(1-q^n)/(1-q)=(a1-anq)/(1-q) q=1时Sn=na1 select * from all_objects connect by rownum<=10; 在某个啥啥帖子里看到 rownum,也就是 COUNT 这个操作是最后生成的,在where之后 SELECT ROWNUM n2 FROM DUAL where level=4 CONNECT BY ROWNUM<=5 意味着. 先 递归第一次,然后level=1 ,此时该level不满足level=4的条件,因此 rownum是0,依然满足rownum<=5. 然后递归第二次,然后level=2,此时该level不满足level=4的条件,因此 rownum是0,依然满足rownum<=5. 然后递归第三次,然后level=3,此时该level不满足level=4的条件,因此 rownum是0,依然满足rownum<=5. 然后递归第四次,然后level=4,此时该level满足level=4的条件,因此 rownum是1,依然满足rownum<=5. 然后递归第五次,然后level=5,此时该level不满足level=4的条件,因此 rownum是1,依然满足rownum<=5. ..... 然后就是天荒地老..海枯石烂..精尽人亡...内存溢出.. 大概是这样 connect by level<=-1也会产生一行,这个要注意 =3不行,必须<=3 SELECT LEVEL,ID,manager_id FROM s_emp START WITH manager_id IS NULL CONNECT BY PRIOR ID=manager_id AND LEVEL<=3; 第1条和第2条要理解递归的本质 dingjun123@ORADB> with t as 2 (select 1 id from dual union all select 2 from dual) 3 select level 4 from t 5 connect by level<3 6 ; LEVEL ---------- 1 2 2 1 2 2 已选择6行。 已用时间: 00: 00: 00.01 dingjun123@ORADB> ed 已写入 file afiedt.buf 1 with t as 2 (select 1 id from dual union all select 2 from dual) 3 select level 4 from t 5 connect by level<3 6* and prior dbms_random.value is not null 7 / LEVEL ---------- 1 2 2 1 2 2 已选择6行。 已用时间: 00: 00: 00.03 已用时间: 00: 00: 00.01 --加上id自连接,必须加上dbms_random,这样相当于每行单独递归的次数,不向上面一样了 dingjun123@ORADB> ed 已写入 file afiedt.buf 1 with t as 2 (select 1 id from dual union all select 2 from dual) 3 select level 4 from t 5 connect by level<3 6 and prior id = id 7* and prior dbms_random.value is not null dingjun123@ORADB> / LEVEL ---------- 1 2 1 2 已选择4行。 已用时间: 00: 00: 00.01 dingjun123@ORADB> ed 已写入 file afiedt.buf 1 with t as 2 (select 1 id from dual union all select 2 from dual) 3 select level 4 from t 5 connect by level<3 6* and prior id = id dingjun123@ORADB> / ERROR: ORA-01436: 用户数据中的 CONNECT BY 循环 未选定行 对于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;SQL> select id,level from t connect by level<2; I LEVEL - ---------- A 1 B 1 C 1 SQL> select id,level from t connect by level<3; I LEVEL - ---------- A 1 A 2 B 2 C 2 B 1 A 2 B 2 C 2 C 1 A 2 B 2 C 2 已选择12行。 SQL> select id,level from t connect by level<4; I LEVEL - ---------- 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 B 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 C 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 已选择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采用了深度优先的算法 我们接着看一个例子,看看在SQL中通过connect by如何将任意一个整数(不要太大就行)拆分为若干个power(2,n)的和的方法。 先构造测试数据: create table ba(n number); insert into ba select 5*rownum from dual connect by rownum<5; commit; select * from ba; 复制代码展示ba中的数据为: N ----------------------- 5 10 15 20 一个得出结果的简单的SQL为 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的拆分结果 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); 复制代码结果为: N LEVEL BITAND(A.N,POWER(2,LEVEL-1)) ---------------------------------------------------------------- 5 1 1 5 2 0 5 3 4 复制代码去掉distinct的sql为 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的大坑的分界线——————————————————————————— 事实上我们有更好的办法来处理: with a as (select n, floor(log(2,n)+1) lc from ba) select a.n, bitand(a.n,power(2,b.rn-1)) from a, (select rownum rn from (select max(lc) mlc from a) connect by level<=mlc )b where rn<=a.lc order by 1,2 复制代码内层SQL先取得所有记录中可拆分出来的power(2,n)中的n最大可能是多少,然后由此构造出序列,最后再做一次关联查询,用限制条件rn<=a.lc限制住每个N中可拆分出来的power(2,n)中的n的最大值,由此可以高效得出结果。 上例实质上与 对多记录按各自指定次数重复 的性质是一样的。 简单总结: 对单记录/单条数据使用connect by,没问题 但对多条记录使用connect by,就会碰到问题,千万要注意。 |
2 select rownum, b.*
3 from (select a.a, sum(a.a) over(order by rownum) - rownum + 1 psum
4 from tmp a) b
5 connect by rownum <= b.psum;
ROWNUM A PSUM
---------- ---------- ----------
1 2 2
2 2 2
3 3 4
4 3 4
5 4 7
6 4 7
7 4 7
8 3 4
9 4 7
9 rows selected
以上红色部分,实在搞不懂为什么会出现在结果集中。
因为按connect by rownum <= psum的条件,8、9都是大于4、7的。但是却出现在了结果集中,不明白这是何原因。
有谁可以解释一下?
您只给出了内层的查询,我不明白这是什么意思......
我能明白这个内层查询的含义,但是不能理解外层查询中rownum=8/9的情况下,为何会出现psum=4/7的情况。
因为此时rownum(8/9) > psum(4/7)了,为何会在connect by rownum <= psum的条件下出现这样的结果?
虽然结果是我想要的,但是不明白这是为什么。
实际上,我是想知道,为何数据会是这样展现的?他的过程是怎么样的?
对于结果:
a b
2 2
3 4
4 7
可以推算,
a=2时,rownum <= 2(b的值),此时rownum=1,则有:
rownum a b
1 2 2
2 2 2--直到rownum=2
a=3时,rownum <= 4,此时rownum=3,则有:
rownum a b
3 3 4
4 3 4--直到rownum=4
a=3时,rownum <= 7,此时rownum=5,则有:
rownum a b
5 4 7
6 4 7
7 4 7--直到rownum=7
于是经过connect by rownum <= psum就可以得到:
1 2 2
2 2 2
3 3 4
4 3 4
5 4 7
6 4 7
7 4 7
8 3 4
9 4 7
但是接下来红色部分的8、9是如何推算的?
rownum a b
8 3 4
9 4 7
还是我这样推算是错误的?
原帖由 CaptainKobe 于 2009-12-15 16:32 发表
SQL> SELECT ROWNUM FROM DUAL CONNECT BY ROWNUM
你这个是静态的,而且是单条的情况下。
考虑多条,多个rownum限定值时,情况就没有那么简单了。
至于你的rownum<=10为什么有11条,我就不得而知了。应该是10条:
SQL> select rownum from dual connect by rownum <= 10 ;
ROWNUM
----------
1
2
3
4
5
6
7
8
9
10
10 rows selected
总结了下规律,发现
不管rownum如何限制,他首先会根据指定的psum值生成若干条数据,直到rownum=这个指定的值时停下,然后转入下一个psum。如rownum=3,而psum=4时,会生成rownum=3和4两条记录。
因此生成的数据为psum-开始时的rownum+1,比如上面的4-3+1=2。
但是,oracle在最后还会根据每个psum自动生成一行,也就是将原始表再复制一次(第一条不复制,不管psum是否有重复都会复制)。
于是生成的记录变成了:4-3+1+1=3了。就是我们所要的行数。
加上LEVEL就明白了:
with tmp as (select 2 a from dual union all select 3 from dual union all select 4 from dual)
select rownum,level, b.*
from (select a.a, sum(a.a) over(order by rownum) - rownum + 1 psum
from tmp a) b
connect by rownum <= b.psum;
ROWNUM LEVEL A PSUM
---------- ---------- ---------- ----------
1 1 2 2
2 2 2 2
3 3 3 4
4 4 3 4
5 5 4 7
6 6 4 7
7 7 4 7
8 1 3 4
9 1 4 7
LEVEL 1是不受CONNECT BY条件限制的。
因为你没有指定START WITH, 所以三行全部都是起点,即结果中你看到的三个LEVEL 1的行。这三行是一定会出来的,不受你CONNECT BY限制。
从2,2起点的结果从LEVEL 1一直到LEVEL 7
其他两行的结果就是你看到的8, 9行,它们没有更高的LEVEL因为从LEVEL 2开始已经不满足CONNECT BY条件,遍历就终止了。
我大概看明白你的意思了。
2 2
3 4
4 7
是不是可以这样理解,我开始查询的时候,rownum=1。然后由于connect by rownum <= 2,于是生成两条记录。
即:
rownum a b
1 2 2
2 2 2
然后,此时rownum=2,rownum再与4比较,发现比4小。于是再生成记录,直到=4为止,即:
rownum a b
1 2 2
2 2 2
3 3 4
4 3 4
然后,此时rownum=4,再与7比较,发现比7小。于是再生成记录,直到=7为止,即:
rownum a b
1 2 2
2 2 2
3 3 4
4 3 4
5 4 7
6 4 7
7 4 7
最后发现第一轮已经轮询完了,然后转入第二轮,由于没有指定start with,
所以接下来的数据都只生成一条,其他的rownum条件都已经不满足rownum<=psum的条件了
理解正确。
CONNECT BY一般是要写前后关系的,即 PRIOR 列名1 = 列名2
没有这个关系的用法经常是在表中只有一行的情况,(比如DUEL),然后用ROWNUM或LEVEL来控制输出的行数。
相关文章推荐
- 使用connect by level/rownum实现连续数字的插入
- 使用connect by level/rownum实现连续数字的插入
- 【CONNECT BY】使用connect by level/rownum实现连续数字的插入
- 通过AEC解读WF的核心原理(四)AEC在内部执行childActivity
- 行转列 SQL ,connect by level
- 看看PHP迭代器的内部执行过程
- oracle 递归 start with connect by level
- for 内部执行原理
- Windows程序内部运行原理 - 孙鑫 - VC++6.0 - Code by SunXin/Remark by HackerJLY
- 执行具有root权限的java代码 am pm的实现过程原理
- 深入理解JavaScript内部原理(1): 执行上下文
- java 执行过程 运行原理
- connect by rownum造表
- 一种使用connect by level转集合的SQL 语句。
- selenium执行内部原理
- JSP的执行过程,运行原理(图)
- Linq的内部执行原理浅析
- 程序内部执行原理
- 内部查询处理器错误: 查询处理器在执行过程中遇到意外错误
- 【转】IIS 内部运行机制及Asp.Net执行过程详解