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中的rownumber,oracle中row_number和rownum的区别和联系(翻译)相关推荐

  1. oracle伪列ROWNUMBER,oracle 中 rownum 和 row_number()

    简单的介绍下oracle 中rownum 和 row_number() 使用,实例演示. 参照:http://www.cnblogs.com/zjrstar/archive/2006/08/31/49 ...

  2. oracle中聚合比较函数,Oracle聚合函数/分析函数

    oracle函数分两类:单行函数.多行函数.多行函数又分为聚合函数.组合函数,参数为数组,数据大小为记录数,这种数组不是普通高级语言的数组,是一种虚拟数组,当记录数大时,会将数据写入硬盘,内存中放的只 ...

  3. 160804、oracle查询:取出每组中的第一条记录

    oracle查询:取出每组中的第一条记录 按type字段分组,code排序,取出每组中的第一条记录 方法一: select type,min(code) from group_info  group ...

  4. oracle中having作用,oracle中having与where的区别

    1.where 不能放在group by 的后面 2.HAVING 是跟GROUP BY 连在一起用的,放在GROUP BY 后面,此时的作用相当于WHERE 3.WHERE 后面的条件中不能有聚集函 ...

  5. oracle分组查询取第一条数据,160804、oracle查询:取出每组中的第一条记录

    oracle查询:取出每组中的第一条记录 按type字段分组,code排序,取出每组中的第一条记录 方法一: select type,min(code) from group_info group b ...

  6. oracle分类函数总结,oracle中分组排序函数用法

    项目开发中,我们有时会碰到需要分组排序来解决问题的情况,如:1.要求取出按field1分组后,并在每组中按照field2排序:2.亦或更加要求取出1中已经分组排序好的前多少行的数据 这里通过一张表的示 ...

  7. oracle 条件排序函数,oracle中分组排序函数

    项目开发中,我们有时会碰到需要分组排序来解决问题的情况,如:1.要求取出按field1分组后,并在每组中按照field2排序:2.亦或更加要求取出1中已经分组排序好的前多少行的数据 这里通过一张表的示 ...

  8. oracle求和分组排序,oracle中分组排序函数用法 - 转

    项目开发中,我们有时会碰到需要分组排序来解决问题的情况,如:1.要求取出按field1分组后,并在每组中按照field2排序:2.亦或更加要求取出1中已经分组排序好的前多少行的数据 这里通过一张表的示 ...

  9. oracle中rowid列,Oracle中的rowid

    ROWID是ORACLE中的一个重要的概念.用于定位数据库中一条记录的一个相对唯一地址值.通常情况下,该值在该行数据插入到数据库表时即被确定且唯一.ROWID它是一个伪列,它并不实际存在于表中.它是O ...

  10. oracle停止一切进程,oracle启动/停止的几种方法以及 启动和停止过程中出错的解决办法...

    一.启动几种方法: 1. sqlplus /nolog connect /as sysdba startup 2. sqlplus /nolog connect /as sysdba startup ...

最新文章

  1. python日历函数_python 怎么定义一个函数,输出日历
  2. 各大公司容器云的技术栈对比
  3. 一个单片机的小问题。
  4. Mysql数据库存储原理
  5. 在PyCharm中自动添加文件头、时间日期等信息
  6. Android检测版本更新
  7. 单片机控制IIC协议EEPROM芯片24C512之模块化编程(持续更新中)
  8. JDK,JRE,JVM的区别
  9. 计算机毕业设计 基于springboot+vue的校园志愿者管理系统
  10. Linux C语言编译警告:control reaches end of non-void function
  11. U8 ActiveX 部件不能创建对象
  12. android 4 源码目录,LXR 目录Android 源码
  13. 银河麒麟 Kylin_s10_sp3安装Oracle11g(FS)(亲测有效)
  14. 组合数学$1排列组合
  15. 解读老黄历--月日时令
  16. 2017计算机系书单推荐(排版更新)
  17. robot—如何调用上传文件的接口,表单传值
  18. 基于Djiango的学生管理系统(含源代码)
  19. 脏读、幻读、不可重复读,傻傻分不清楚
  20. 对于配置JAVA_HOME

热门文章

  1. MySQL索引有序性分析
  2. 【MySQL】 锁机制:InnoDB引擎中锁分类以及表锁、行锁、页锁详解
  3. 2020 不忘初心 继续前行
  4. php 正则获取邮箱后缀名,php中邮箱地址正则表达式实现与详解
  5. 每日新闻:腾讯与Line携手在日本提供移动支付服务;阿里组织架构调整 行癫任阿里云智能总裁;每周要工作80小时才有可能改变世界...
  6. pandas.melt()详解
  7. NYOJ:33-蛇形填数
  8. 计算机系外文文献题目,计算机专业外文文献翻译.doc
  9. Android制作logo
  10. 【数字IC/FPFA】时序约束--时钟约束