http://www.tuicool.com/articles/bI3IBv

附问题:有以下一个SQL语句:

SELECT  *
FROM (
SELECT t.*, row_number() OVER (ORDER BY ID) rn
FROM mytable t
)
WHERE rn BETWEEN :start and :end

sql中的order by语句大大降低了处理的速度,如果把order by去掉,相应的执行计划会大大地提高。如果换成下面的sql:

SELECT  t.*, row_number() OVER (ORDER BY ID) rn
FROM mytable t
WHERE rownum BETWEEN :start and :end

很明显,这个sql是错的,根本查询不了正确的数据信息。是否有其它的方法可以提高查询速度? 
针对以上问题,就必须要了解一下关于row_number和rownum的区别,以及如何来运用这些信息。

首先了解一下rownum是如何进行工作的,根据oracle的官方文档: 
如果对rownum进行大于比较,这个比较将直接返回false。如,下列sql语句将不能返回任何数据信息:

SELECT  *
FROM employees
WHERE ROWNUM > 1

在查询中,第一条被命中的数据将赋予一个伪列rownum为1,那么这个条件就为false。第二条被命中的数据由于第一条的false将重新成为第一条数据,那么仍然赋值为1,显示这个条件仍然为false。后续所有的数据将重复执行这个逻辑,最后一条数据也没有返回。

这就是为什么之前的第2个查询,应该转换为以下的sql语句:

SELECT  *
FROM (
SELECT t.*, ROWNUM AS rn
FROM mytable t
ORDER BY
paginator, id
)
WHERE rn BETWEEN :start and :end

接下来,需要通过创建一些临时数据表来查看这个sql语句的执行性能,我们将创建临时表,追加索引,然后填充数据,最后分析这个sql语句的查询信息。

CREATE TABLE mytable (
id NUMBER(10) NOT NULL,
paginator NUMBER(10) NOT NULL,
value VARCHAR2(50)
)
/
ALTER TABLE mytable
ADD CONSTRAINT pk_mytable_id PRIMARY KEY (id)
/ CREATE INDEX ix_mytable_paginator_id ON mytable(paginator, id)
/ INSERT
INTO mytable(id, paginator, value)
SELECT level, level / 10000, 'Value ' || level
FROM dual
CONNECT BY
level <= 1000000
/ COMMIT
/ BEGIN
DBMS_STATS.gather_schema_stats('"20090506_rownum"');
END;
/

这个Sql语句创建一个包括100万条数据的表,并且创建一个联合索引. 
同时,在这个查询中,patinator字段是不是惟一的,是为了在之后展示这样一种现象:
在查询中,某些数据可能在不同的分页查询中出现多次,而某些数据则可能根据不会被查询出 
这就是所谓的分页混乱。

然后,分别使用row_numer和rownum分别进行查询,返回从900001到900010之间的10条数据信息。 
row_number()

SELECT  *
FROM (
SELECT t.*, ROW_NUMBER() OVER (ORDER BY paginator, id) AS rn
FROM mytable t
)
WHERE rn BETWEEN 900001 AND 900010
ID PAGINATOR VALUE RN
900001 90 Value 900001 900001
900002 90 Value 900002 900002
900003 90 Value 900003 900003
900004 90 Value 900004 900004
900005 90 Value 900005 900005
900006 90 Value 900006 900006
900007 90 Value 900007 900007
900008 90 Value 900008 900008
900009 90 Value 900009 900009
900010 90 Value 900010 900010
10 rows fetched in 0.0005s (0.8594s)
SELECT STATEMENT
VIEW
WINDOW NOSORT STOPKEY
TABLE ACCESS BY INDEX ROWID, 20090506_rownum.MYTABLE
INDEX FULL SCAN, 20090506_rownum.IX_MYTABLE_PAGINATOR_ID

rownum

SELECT  *
FROM (
SELECT t.*, ROWNUM AS rn
FROM (
SELECT *
FROM mytable
ORDER BY
paginator, id
) t
)
WHERE rn BETWEEN 900001 AND 900010
ID PAGINATOR VALUE RN
900001 90 Value 900001 900001
900002 90 Value 900002 900002
900003 90 Value 900003 900003
900004 90 Value 900004 900004
900005 90 Value 900005 900005
900006 90 Value 900006 900006
900007 90 Value 900007 900007
900008 90 Value 900008 900008
900009 90 Value 900009 900009
900010 90 Value 900010 900010
10 rows fetched in 0.0005s (0.7058)
SELECT STATEMENT
VIEW
COUNT
VIEW
TABLE ACCESS BY INDEX ROWID, 20090506_rownum.MYTABLE
INDEX FULL SCAN, 20090506_rownum.IX_MYTABLE_PAGINATOR_ID

从上文中,可以看出,使用rownum的查询速度略快于row_number函数。 
然后再看一个row_number查询,可以看出oracle足够的智能,它可以通过使用联合索引而避免进行排序操作,然后通过使用stopkey操作,可以直接快速查找到相应的数据信息。 
rownum查询也同样使用索引,但并没有利用stopkey条件,只是简单的计数操作。 

么,能否同样让rownum使用stopkey呢。在之前的查询中,oracle并不知道这个rn就是在内层查询rownum的别名,我们可以重写查询,
在外层查询中使用rownum,这样就可以在外层利用stopkey条件了。这就是我们常见的oracle3层分页的变形:

SELECT  *
FROM (
SELECT t.*, ROWNUM AS rn
FROM (
SELECT *
FROM mytable
ORDER BY
paginator, id
) t
)
WHERE rn >= 900001
AND rownum <= 10
ID PAGINATOR VALUE RN
900001 90 Value 900001 900001
900002 90 Value 900002 900002
900003 90 Value 900003 900003
900004 90 Value 900004 900004
900005 90 Value 900005 900005
900006 90 Value 900006 900006
900007 90 Value 900007 900007
900008 90 Value 900008 900008
900009 90 Value 900009 900009
900010 90 Value 900010 900010
10 rows fetched in 0.0005s (0.4714s)
SELECT STATEMENT
COUNT STOPKEY
VIEW
COUNT
VIEW
TABLE ACCESS BY INDEX ROWID, 20090506_rownum.MYTABLE
INDEX FULL SCAN, 20090506_rownum.IX_MYTABLE_PAGINATOR_ID

在这个查询中,oracle利用了stopkey,同时速度只有471ms,比原来更快。

如果row_number和rownum使用同样的执行计划,但为什么rownum明显更快呢。 
这是因为:oracle的历史实在是太久了,而不同的时间导致相同的特性却有不同的效果。

rownum在oracle6中被引进,发布时间为1988年,在当时什么资源和条件都不满足的情况下,作为一个简单的计数器,被认为是非常简单和高效的。 
而随着时代的发展,更多的需求被提及出来,这时,一个相当于但功能比rownum更强大的函数被引入,这就是row_number函数,它从oracle9i开始被引进。这时,效率已经不再是惟一的条件了,所以row_number的实现也不再以效率为惟一的指标了。

当然,如果你有更多的要求,如分组排序等,则需要使用row_number函数,但如果你仅仅是简单的分页查询,建议使用
rownum,这也是为什么在现在的时代rownum还是这么流行(据说在oracle12c中有offset分页操作符了,内部同样使用
row_number函数,这样rownum可以退休了)

以下是英文原文:http://explainextended.com/2009/05/06/oracle-row_number-vs-rownum/

oracle中row_number和rownum的区别和联系(翻译)的更多相关文章

  1. oracle的row_number()和rownum

    row_number() 函数和rownum的介绍: 1.row_number() 方法的格式: row_number()over([partition by col1] order by col2) ...

  2. oracle中函数和存储过程的区别和联系【转载竹沥半夏】

    oracle中函数和存储过程的区别和联系[转载竹沥半夏] 在oracle中,函数和存储过程是经常使用到的,他们的语法中有很多相似的地方,但也有自己的特点.刚学完函数和存储过程,下面来和大家分享一下自己 ...

  3. oracle中函数和存储过程的区别和联系

    oracle中函数和存储过程的区别和联系 在oracle中,函数和存储过程是经常使用到的,他们的语法中有很多相似的地方,但也有自己的特点.刚学完函数和存储过程,下面来和大家分享一下自己总结的关于函数和 ...

  4. oracle中row_number() over()分析函数用法

    row_number()over(partition by col1 order by col2)表示根据col1分组,在分组内部根据col2排序,而此函数计算的值就表示每组内部排序后的顺序编号(组内 ...

  5. Oracle中的rowid rownum

    1. rowid和rownum都是虚列 2. rowid是物理地址,用于定位oracle中具体数据的物理存储位置 3. rownum则是sql的输出结果排序,从下面的例子可以看出其中的区别. rowi ...

  6. (转)ORACLE中SID和SERVICE_NAME的区别

    背景:之前一直分不清plsql和程序中配置文件url之间的连接,想当然的认为service_name 和jdburl后面的实例相对应,直到出错的这一天,通过这篇博客,彻底扫除了盲点. 1 问题 1.1 ...

  7. 转://Oracle中User和Schema的区别和联系

    今天在阅读Oracle官方文档的时候,读到schema的基本概念,这就让我产生了一个疑问:user和schema两者之间到底有什么区别?为了更深层次的理解二者之间的区别和联系,以下是官方文档中关于us ...

  8. Oracle中Restore和Recovery的区别

    一.参考解释一 在Oracle的备份与恢复的知识点中,经常会出现Restore 和 Recovery两个词. 由于这两个词在字典中的解释很接近,困扰了我很久.直到我在Oracle的官方文档中看到了以下 ...

  9. Oracle中用户和方案的区别

    从定义中我们可以看出方案(Schema)为数据库对象的集合,为了区分各个集合,我们需要给这个集合起个名字,这些名字就是我们在企业管理器的方案下看到的许多类似用户名的节点,这些类似用户名的节点其实就是一 ...

随机推荐

  1. java从基础知识(八)泛型

    1.什么是泛型? 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法 ...

  2. 如何在centos下部署Node环境

    从今年开始,养车之家的PC 和 M端 的站点 ,服务器从tomcat全部改成了Node,无论在测试环境.准生产.正式环境,我们都需要在服务器进行Node的部署,在这里我给大家详细说明下Node在lin ...

  3. Javascript面向对象特性实现封装、继承、接口详细案例——进级高手篇

    Javascript面向对象特性实现(封装.继承.接口) Javascript作为弱类型语言,和Java.php等服务端脚本语言相比,拥有极强的灵活性.对于小型的web需求,在编写javascript ...

  4. react+redux官方实例TODO从最简单的入门(2)-- 增

    虽然官网的TodoList的例子写的很详细,但是都是一步到位,就是给你一个action,好家伙,全部都写好了,给你一个reducer,所有功能也是都写好了,但是我们这些小白怎么可能一下就消化那么多,那 ...

  5. 微信小程序常见问题集合(长期更新)

    最新更新: 新手跳坑系列:推荐阅读:<二十四>request:fail错误(含https解决方案)(真机预览问题 跳坑指南<七十>如何让微信小程序服务类目审核通过 跳坑六十九: ...

  6. 提交本地项目到github

    要托管到github,那你就应该要有一个属于你自己的github帐号,所以你应该先到github.com注册 打开浏览器 在地址栏输入地址:github.com 填写用户名.邮箱.密码 点击Sign ...

  7. sql练习(mysql版)

    学生表student 老师表teacher 课程表course 分数表test 1.数学比语文成绩高的所有学生 select a.sid from () a, () b where a.sid=b.s ...

  8. testng 失败自动截图

    testng执行case failed ,testng Listener会捕获执行失败,如果要实现失败自动截图,需要重写Listener的onTestFailure方法 那么首先新建一个Listene ...

  9. java 深入技术七(类的结构和反射)

    1.java反射(reflect) java 类的结构 java反射机制就是把java类按结构分解,每一部分对应特定的反射类 java反射机制允许运行时加载,探知和使用在编译期间完全未知的classe ...

  10. PHP新手常见的一些不好习惯(抄的 有待理解)

    1.不写注释(是个好习惯,不过也没必要每个语句都要写) 2.不使用可以提高生产效率的IDE工具 3.不使用版本控制 4.不按照编程规范写代码 5.不使用统一的方法 6.编码前不去思考和计划 7.在执行 ...