rownum与order by 子句

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Carrots_vegetables/article/details/82632909

rownum与order by 子句经常会在需要排序和分页的时候混合使用,如果不知道其中的关系就会出现莫名其妙的结果。
下文是网上抄的但确实帮助本人解决了问题。特别是第二大点rownum与order by 子句的执行顺序关系和解决办法。

一、 rownum的特点

rownum的特点是在select语句查询过程中获得一条符合条件的数据行放入结果集合中时,oracle系统就会自动编一个号,即一个rownum值。这里要强调一点的是,一个结果集合里的任何时刻,都是有一个rownum值为1的数据行的,其后的数据行编号依次为2,3,。。。。以此类推的。如果那一条rownum值为1的数据行从结果集合里剔除了,跟在其后为2的数据行自动变为1,其他行也跟着做相应变化。

例如,下面语句:select rownum,t1. * from t1 where id>2 and rownum > 1。在执行这条语句时,oracle系统首先将它当作select rownum,t1. * from t1 where id>2进行执行,即忽略了rownum > 1部分。之所以会如此,是因为如下原因:

首先,我们要有一个概念,即每个select查询语句都有对应的一个结果集。而不同(条件)的select查询语句对应的结果集可能是不一样的。比如,数据行A,通过select语句E被找到时,发现自己是第5个被找到的,即它在select语句E对应的结果集的rownum值为5;而通过select语句F被找到时,发现自己是第2个被找到的,即它在select语句F对应的结果集的rownum值为2。也就是说,一个数据行的rownum值为多少是有一个前提的,即该数据行的rownum值针对哪个结果集来说的。所以,在数据行还没有被select语句找到前没有rownum这个概念或说是属性的。故而,在执行这条语句时,oracle系统首先将它当作select rownum,t1. * from t1 where id>2进行执行,即忽略了rownum > 1部分。

通过执行select rownum,t1. * from t1 where id>2语句找到一条数据行后,oracle系统先是对该数据行进行编号,也就是给它设置rownum的值。再通过rownum > 1这个条件对其进行进一步的筛选。具体地说,也就是,对表上的每一条数据行,oracle系统都是首先通过select rownum,t1. * from t1 where id>2语句进行 筛选,如果符合筛选条件的,就放入select rownum,t1. * from t1 where id>2 and rownum > 1语句对应的结果集(相当于一个放数据行这个东西的一个容器)中,接着,给该数据行编号(赋值给该行的rownum变量),最后,看该数据行的rownum值是否符合rownum > 1这个条件,符合的话,就留在结果集中。 不符合,就从结果集中去掉该数据行。这样,对表上的一条数据行的筛选过程就完成了,接着对相邻的下一条数据行再重复进行上述的过程。

这里,我们发现select rownum,t1. * from t1 where id>2 and rownum > 1执行的结果是未选定行,这是为什么呢?

因为当通过执行select rownum,t1. * from t1 where id>2语句找到第一条数据行后,oracle系统先是对该数据行进行编号为1,也就是给它设置rownum值为1。再通过rownum > 1这个条件对其进行进一步的筛选,发现该数据行的rownum为1,不符合条件,所以,就从结果集中去掉该数据行。(此时结果集里还是没有一条数据行)

接着,通过执行select rownum,t1. * from t1 where id>2语句找到第二条数据行后,oracle系统先是对该数据行进行编号,值依然为1,也就是给它设置rownum值为1,因为此时结果集里还是没有一条数据行,编号值也就还没使用过,当然还是从1开始编号。再通过rownum > 1这个条件对其进行进一步的筛选,发现该数据行的rownum为1,也不符合条件,所以,也从结果集中去掉该数据行。(此时结果集里还是没有一条数据行)

这样,全表扫描完之后, 结果集里是没有一条数据行的。

附加说明:

1、先好好理解 rownum 的意义吧,即ROWNUM是一个序列,是oracle数据库从数据文件或缓冲区中读取数据的顺序。因为ROWNUM是对结果集加的一个伪列,即先查到结果集之后再加上去的一个列 (强调:先要有结果集)。简单的说 rownum 是对符合条件结果的序列号。它总是从1开始排起的。所以你选出的结果不可能没有1,而有其他大于1的值。所以您没办法期望得到下面的结果集:

11 aaaaaaaa
12 bbbbbbb
13 ccccccc
……………..

2、(物理)Rowid是在记录插入表时的顺序给记录顺序分配的,即Rowid在数据行插入表时就有了。

而rownum这是在对应select语句的结果集(结果集或不准确说是在内存中的)中产生的。

二 、rownum与order by 子句的执行顺序关系

如果select语句(非嵌套查询形式的)有order by子句,则order by子句一般都是最后一步执行的。但是如果order by子句里的字段被设置了主键约束或是被设置索引了,那么order by子句执行之后,oracle系统还会重新对结果集的rownum值进行编号。也就是说,order by子句里的字段被设置了主键约束(或是被设置索引)的select语句,先是在数据行选入结果集时对其进行一次编号外,还会在order by子句执行之后,还会重新对结果集的rownum进行编号。

例如,以下例子:

表user_info开始时未设置主键约束,有sql查询语句:

select ROWNUM rn, ID, YYB, XM,MC from user_infou order by ID ;

其所得的结果如下:
RN ID YYB XM MC


3 49 某证券总部 管理员 测试
4 96 某证券总部 管理员 持有上港10000股以上
5 102 某证券总部 管理员 十年规划
14 105 某证券总部 管理员 开发渠道为上海
11 106 某证券总部 管理员 万科A
12 107 某证券总部 管理员 11
13 108 某证券总部 管理员 今天过生日的客户
2 109 某证券总部 管理员 客户状态正常
6 110 某证券总部 管理员 无交易
7 111 某证券总部 管理员 OA
8 112 某证券总部 管理员 幸运客户
9 113 某证券总部 管理员 风险型
10 114 某证券总部 管理员 tst
22 115 白沙网上交易 安昌彪 安客户正常
1 118 某证券总部 管理员 213
18 119 某证券总部 管理员 客户号包含1008
17 120 某证券总部 管理员 aaa
19 123 某证券总部 管理员 ssssssss
20 124 某证券总部 管理员 www
21 126 某证券总部 管理员 123123
15 127 某证券总部 管理员 1212
16 128 某证券总部 管理员 aaaaaa

22 rows selected

我们发现结果里的ID列的值是有序排列的,但是RN列的值则是杂乱无序的。这个就是因为order by子句里的字段未被设置了主键约束(或是未被设置索引)的select语句在数据行选入结果集时对其进行一次编号后,在order by子句执行之后,不会重新对结果集的rownum进行编号。

接着,我们对表user_info进行设置主键约束,alter table user_info addconstraint pk_user_info primary key(id)。这次,使用如下的sql语句:

select ROWNUM rn, ID, YYB, XM,MC from user_infou Where rownum>10 and rownum<=20 order by ID ;

结果如下:
RN ID YYB XM MC


11 112 某证券总部 管理员 幸运客户
12 113 某证券总部 管理员 风险型
13 114 某证券总部 管理员 tst
14 115 白沙网上交易 安昌彪 安客户正常
15 118 某证券总部 管理员 213
16 119 某证券总部 管理员 客户号包含1008
17 120 某证券总部 管理员 aaa
18 123 某证券总部 管理员 ssssssss
19 124 某证券总部 管理员 www
20 126 某证券总部 管理员 123123

10 rows selected

我们发现结果里的ID列的值是有序排列的,RN列的值也是有序的。这个就是因为order by子句里的字段被设置了主键约束(或是被设置索引)的select语句,先是在数据行选入结果集时对其进行一次编号外,还会在order by子句执行之后,还会重新对结果集的rownum进行编号。

当然,如果我们不用在order by子句里的字段设置主键约束或是设置索引的方式,使结果集排序后再重新对结果集的rownum值进行编号,那还可以使用嵌套查询的方式。

Sql代码查询如下:

SELECT *   

FROM(   

SELECT ROWNUM RN,TA.*   

FROM(  

select ROWNUM rn, ID, YYB, XM, MC from user_info  order by ID ;

)TA WHERE ROWNUM <=  20  

)WHERE RN > 10 

结果也是如下:
RN ID YYB XM MC


11 112 某证券总部 管理员 幸运客户
12 113 某证券总部 管理员 风险型
13 114 某证券总部 管理员 tst
14 115 白沙网上交易 安昌彪 安客户正常
15 118 某证券总部 管理员 213
16 119 某证券总部 管理员 客户号包含1008
17 120 某证券总部 管理员 aaa
18 123 某证券总部 管理员 ssssssss
19 124 某证券总部 管理员 www
20 126 某证券总部 管理员 123123

10 rows selected

三 、关于嵌套查询的执行效率对比

这里,顺便再讲下利用上述嵌套查询来分页显示为什么会比下面 查询Sql语句效率要高:

 select * from (select ROWNUM rn, ID, YYB, XM,MC from    user_infou  order by ID )Where rn>10 and rn<=20;  

嵌套查询方法相比之下拥有较高的效率,主要体现在WHERE ROWNUM <= 20这句上。

选择第11到20条记录存在两种方法,一是嵌套查询方法,它正是在查询的第二层通过ROWNUM <= 20来控制最大值,在查询的最外层控制最小值。而第二种方法是去掉嵌套查询方法第二层的WHERE ROWNUM <= 20语句,在查询的最外层控制分页的最小值和最大值。

一般来说,第一个查询(即嵌套查询)的效率比第二个高得多,这是由于CBO 优化模式下,Oracle可以将外层的查询条件推到内层查询中,以提高内层查询的执行效率。对于第一个查询语句,第2层的查询条件WHERE ROWNUM <= 20就可以被Oracle推入到内层查询中,这Oracle查询的结果一旦超过了ROWNUM限制条件,就终止查询将结果返回了。

而第二个查询语句,由于查询条件Where rn>10 and rn<=20是存在于查询的第二层,而Oracle无法将第二层的查询条件推到最内层(即使推到最内层也没有意义,因为最内层查询不知道RN代表什么)。因此,对于第二个查询语句,Oracle最内层返回给中间层的是所有满足条件的数据,而中间层返回给最外层的也是所有数据。数据的过滤在最外层完成,显然这个效率要比第一个查询低得多。

这种分页查询(即嵌套查询)对于单表查询、多表查询一样有效。

猜你喜欢

转载自blog.csdn.net/Carrots_vegetables/article/details/82632909