博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Oracle伪列rownum的学习【转载】
阅读量:6278 次
发布时间:2019-06-22

本文共 3000 字,大约阅读时间需要 10 分钟。

hot3.png

Hibernate的分页,里边讲到了Oracle下Hibernate是使用rownum来实现分页的。
Oracle 9i Complete Reference中关于rownum的叙述是:
ROWNUM returns the sequence number in which a row was returned when first① selected② from a table. The first row has a ROWNUM of 1③, the second is 2, and so on. Note, however, that even a simple order by in the select statement my disorder the ROWNUMs, which are assigned to the rows before any ordering takes place.
对于这段描述,有3处需要特别注意的地方(定义中①②③标注的部分)。
详细解释如下:
1.ROWNUM是在记录第一次从表中被select出来的时候赋的值。里边的first就是这个第一次的意思。
2.ROWNUM是记录从表中被select出来时赋的值。而不是选出来后,对被选出来的记录进行某些操作后才赋值。这里经常出现的一个问题是如何用order by 和rownum结合解决TOP-N;
3.ROWNUM是对应一次select活动的,而不是对应一个表的。
在包含子查询的查询中,每次查询活动都可以产生一个ROWNUM,这些相同记录的ROWNUM是不同的;
4.ROWNUM是在记录被select出来的时候产生的。
第1条记录的ROWNUM值为1,第2条记录为2….如果查询条件包含了ROWNUM并且与ROWNUM取值规律矛盾,则不会有任何记录被选出。
TOP-n
ROWNUM是在select出记录的时候生成的,发生在order by 之前。如果试图通过order by 和rownum来解决TOP-N问题,就需要特别小心这一点。
例如
SELECT Empno, Ename, Job, Mgr, Hiredate, Sal
      FROM Emp
      ORDER BY Sal DESC WHERE ROWNUM <11
是不会选出Sal最小的10条记录的。
ROWNUM 是在记录被select出来时产生的,它先与任何排序的。上述语句的实际执行步骤与SQLServer中的TOP字句是不同的。
为了select出工资最高的5个员工,你必须通过子查询强制在使用ROWNUM条件之前进行排序。这就是解决top-N问题的常用办法。
SELECT Empno, Ename, Job, Mgr, Hiredate, Sal
   FROM
   (SELECT Empno, Ename, Job, Mgr, Hiredate, Sal
      FROM Emp
      ORDER BY NVL(Sal, 0) DESC)
   WHERE ROWNUM < 6;
另外,在网上找到帖子提到了例外情况:如果order by的字段上有主键或索引,则oracle先排序,然后再rownum。我没有做验证,但我猜想,这应该体现了oracle的内部实现机制:如果order by字段上没有索引,则先选出满足条件的所有记录,然后再排序。如果有索引,则选出来的时候,记录就是字段上的索引按顺序出来的。所以,ROWNUM是在排序的基础上分配的。
不过,在javaeye,找到一篇文章(链接为:
http://forum.javaeye.com/viewtopic.php?=9681&postdays=0&postorder=asc&start=0)。
作者说即使用子查询,当满足一定条件的时候,这个分页机制也是不正确的。这篇帖子是2004年的,后来讨论的人就不多了,robbin等大虾也没有来讨论。是否像他所说,我还得验证验证。
指定记录ROWNUM的上下限
ROWNUM是一个伪字段,是在记录被select出来时产生的,并不是实际存在的字段。它的取值从1开始的。
ROWNUM直接出现在where字句中的合理用法有3种:
rownum = 1; rownum < n; rownum <= n。除此之外的任何形式都是得不到任何记录的。例如 rownum=2; rownum > 5。
下边详细说明原因。rownum = 2 得不到记录,因为查询返回的第一条记录其ROWNUM必须为1。但是rownum=2又不允许这样的记录被返回。因此,它永远不会返回第一条记录,也永远无法找到满足rownum=2的记录。
对于rownum>5。This is because the pseudo column rownum never reaches 6. Rownum counts actually returned rows. In order for where rownum > 5 to be true, 5 rows must already have returned, but they are not, because these were excluded through exactly this where clause.
个人觉得在WHERE里使用ROWNUM让人感到有点乱:ROWNUM既作为条件同时又作为select出来记录时产生的数据(类似select出来的字段)。而ROWNUM又不是一个真正存在的字段,只是在select出来记录的时候产生的。似乎有一点先有鸡还是先有蛋的意思。
这里,可以破坏这个乱的根源:把ROWNUM这个伪字段变成一个真正的字段。方法是子查询。在子查询中把ROWNUM select出来,然后定义一个别名。此时,ROWNUM就作为查询结果集的一个真正字段存在了。之后,在外层查询中用这个别名来做任何条件都可以了。如下述语句所示:
select name , price
from (
   Select rownum r, name, price from items)
where r>5
This works because Oracle first evaluates the inner select statement and returns all records with an increasing rownum. The outer where clause can then select the rows it needs.
不过,这种方法有一个问题,就是如果表的数据量很大,会导致查询结果耗用大量的系统资源。
子查询的ROWNUM
select tt.*, rownum  num2 from (select rownum num_, t.* from t_moi t ) tt where tt.num_ >2 and tt.num_<6
这里边的num2和num_的值是不相同的,也就是两次select有各自的rownum。
总结
看来,用子查询可以解决ROWNUM的很多问题。

转载于:https://my.oschina.net/u/185335/blog/64125

你可能感兴趣的文章
小五思科技术学习笔记之扩展访问列表
查看>>
使用Python脚本检验文件系统数据完整性
查看>>
使用MDT部署Windows Server 2003 R2
查看>>
Redhat as5安装Mysql5.0.28
查看>>
通过TMG发布ActiveSync
查看>>
Web服务器的配置与管理(4) 配置访问权限和安全
查看>>
Linux系统安装VMware Tools
查看>>
asp.net 页面右下角弹出类似QQ或MSN的消息提示
查看>>
游戏开发经常使用算法概述
查看>>
EDM制作要点
查看>>
爆牙齿的Web标准面试考题II(iPhone SMS/iChat UI的Web标准实现)
查看>>
XMOVE3.0手持终端——软件介绍(二):在2KB内存的单片机上实现的彩屏GUI控件库
查看>>
MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)
查看>>
找小于N 的所有质数
查看>>
Windows下的Jupyter Notebook 的介绍(写给新手)(图文详解)
查看>>
iOS开发-CocoaPods实战
查看>>
JS组件系列——Bootstrap 树控件使用经验分享
查看>>
HTML-color:rgb()-颜色渐进
查看>>
数据库实例: STOREBOOK > 表空间 > 编辑 表空间: UNDOTBS1
查看>>
Mcad学习笔记之异步编程(AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用小总结)...
查看>>