转自:http://bbs.csdn.net/topics/370033478

对于Oracle中分页排序查询语句执行效率的比较分析
作者:lzgame
在工作中我们经常遇到需要在Oracle中进行分页、排序、查询的组合SQL语句,举例来说,通常我们会这样写:(假定表test中id是主键,并且id从1开始没有间断顺序排列)

1. SELECT * FROM (
      SELECT id,a1,a2,a3,a4,a5,a6,a7,a8,a9, ROWNUM AS rn FROM test
      WHERE a1 LIKE '%%' AND ROWNUM<=1000000
      ORDER BY id
    ) t2 WHERE  rn>=999990;

但是由于ROWNUM是一个伪列,Oracle会首先查询满足ROWNUM<=1000000条件的记录,然后再对得到的记录进行排序,这就导致我
们并不能获得期望的排序结果。本应得到id为999990-1000000,实际上却得到了一些无规律并令人困惑的id结果。于是我做了以下改进;

2. SELECT * FROM (
      SELECT id,a1,a2,a3,a4,a5,a6,a7,a8,a9, ROWNUM AS rn FROM (
        SELECT id,a1,a2,a3,a4,a5,a6,a7,a8,a9 FROM test
        WHERE a1 LIKE '%%'
        ORDER BY id
      ) t1 WHERE  ROWNUM<=1000000
    ) t2 WHERE  rn>=999990;

显然,通过经典的三层分页排序查询结构,我确实得到了想要的结果。但是在数据量很大的数据表中,由于需要首先取得排序后的全部数据集,导致了执行效率的极
速降低,直至无法忍受。为了提高性能,我在网上查阅了很多资料,其中有一种说法是:当排序条件使用的关键字是主键或索引,并在WHERE子句中先于
ROWNUM使用过该关键字时,我们就可以采用1号语句获得想要的结果了,于是我改写了1号语句并进行了测试:

3. SELECT * FROM (
      SELECT id,a1,a2,a3,a4,a5,a6,a7,a8,a9, ROWNUM AS rn FROM test
      WHERE id>=0 AND ROWNUM<=1000000
      ORDER BY id
    ) t2 WHERE  rn>=999990;

我惊喜的发现确实有效,我成功了,但惊喜并没有持续多久,因为当我把查询条件“a1 LIKE '%%'”也放回WHERE语句中时,一切又恢复了原状。
于是新一轮的资料查找又开始了。但网上的资料似乎始终没有脱离以上3种语句的范围,在漫长的查找后我快要放弃了,忽然在一条论坛回复中我看见了新的曙光:
用WHERE…IN语句。我立刻改写出了4号语句:

4.SELECT * FROM test 
   WHERE id IN (
      SELECT id FROM (
        SELECT id, ROWNUM AS rn FROM (
          SELECT id FROM test
          WHERE a1 LIKE '%%'
          ORDER BY id
        ) t1 WHERE  ROWNUM<=1000000
      ) t2 WHERE  rn>=999990 
    );

这条语句通过减少最内层SELECT语句获得的数据量(仅保留必须的id),极大地提高了查询性能。但是WHERE…IN语句由于需要遍历数据表,也就是
说在本语句中每查询出一个id,Oracle就需要在最后的WHERE…IN语句搜索一次并把它挑出来,所以WHERE…IN语句本身的效率并不高,本语
句依然存在着效率提升的空间,那么该怎么做呢?答案就是ROWID伪列。什么是ROWID伪列呢?用最简单的话说,ROWID就是该数据行的绝对物理地
址,在百度百科上我们可以查到索引就是通过ROWID来记录数据位置的。于是我们的WHERE…IN语句不再需要遍历数据表,不再需要通过一次次的检索来
收集数据了,因为它得到了最终数据的直接物理地址。从这个意义上讲,WHERE…IN语句成为了最高效的语句。
语句改写如下:

5.SELECT * FROM test 
   WHERE ROWID IN (
      SELECT rid FROM (
        SELECT rid, ROWNUM AS rn FROM (
          SELECT ROWID rid FROM test
          WHERE a1 LIKE '%%'
          ORDER BY id
        ) t1 WHERE  ROWNUM<=1000000
      ) t2 WHERE  rn>=999990 
    );

令人困惑的是,虽然5号语句检索出来的数据范围是正确的,但是最终的顺序是被打乱的,也就是说id虽然范围是在999990-1000000中,但相互间是没有顺序的,当然到这一步已经没有什么难度了,于是我写出了最终的语句:

6. SELECT * FROM test 
   WHERE ROWID IN (
      SELECT rid FROM (
        SELECT rid, ROWNUM AS rn FROM (
          SELECT ROWID rid FROM test
          WHERE a1 LIKE '%%'
          ORDER BY id
        ) t1 WHERE  ROWNUM<=1000000
      ) t2 WHERE  rn>=999990 
    ) ORDER BY id;
至此一切OK,为了对提升的效率有个直观的理解,我又对1、2、4、6号语句进行了测试(测试数据表test共1011003条记录),各测试三次去平均值,结果如下:

1号语句(两层嵌套,而且结果并不符合预期):21.98秒
2号语句(三层嵌套,结果符合预期,但实在是太慢了。。。):48.62秒
4号语句(采用WHERE id IN …,结果符合预期,效率极大提高):11.49秒
6号语句(采用WHERE ROWID IN …,结果符合预期,效率最高):5.98秒

以上是我对Oracle中分页排序查询语句执行效率的一点理解,如果有什么错误的地方请大家指正,谢谢!

对于Oracle中分页排序查询语句执行效率的比较分析的更多相关文章

  1. Oracle中的一些查询语句及其执行顺序

    查询条件: 1)LIKE:模糊查询,需要借助两个通配符,%:表示0到多个字符:_:标识单个字符. 2)IN(list):用来取出符合列表范围中的数据. 3)NOT IN(list): 取出不符合此列表 ...

  2. 常用oracle中系统表查询语句

    sqlplus / as sysdbaSQL>select status from v$instance;1.查看最大连接数show parameter processes;2.查询oracle ...

  3. Oracle中分页查询语句

    Oracle分页查询语句使我们最常用的语句之一,下面就为您介绍的Oracle分页查询语句的用法,如果您对此方面感兴趣的话,不妨一看. Oracle分页查询语句基本上可以按照本文给出的格式来进行套用.O ...

  4. Oracle rownum 分页, 排序

    Oracle rownum 分页, 排序 什么是rownum, rownum的生成, rownum相关的符号操作 Rownum是oracle生成结果集时得到的一个伪列, 按照读出行的顺序, 第一条ro ...

  5. oracle中分页函数写法

    1.常见的分页查询语句: 查询21到40条之间的数据:SELECT *FROM (select UI.*,ROWNUM RN FROM (select * from user_info) AWHERE ...

  6. SQL逻辑查询语句执行顺序 需要重新整理

    一.SQL语句定义顺序 1 2 3 4 5 6 7 8 9 10 SELECT DISTINCT <select_list> FROM <left_table> <joi ...

  7. python 3 mysql sql逻辑查询语句执行顺序

    python 3 mysql sql逻辑查询语句执行顺序 一 .SELECT语句关键字的定义顺序 SELECT DISTINCT <select_list> FROM <left_t ...

  8. mysql第四篇--SQL逻辑查询语句执行顺序

    mysql第四篇--SQL逻辑查询语句执行顺序 一.SQL语句定义顺序 SELECT DISTINCT <select_list> FROM <left_table> < ...

  9. MySQL查询语句执行过程及性能优化(JOIN/ORDER BY)-图

    http://blog.csdn.net/iefreer/article/details/12622097 MySQL查询语句执行过程及性能优化-查询过程及优化方法(JOIN/ORDER BY) 标签 ...

随机推荐

  1. Ionic Android开发环境搭建 下

    上篇 Ionic 安装完成了,由于要开发Android,所以必不可少需要继续搭建Android开发环境. 首先,下载并安装JDK.然后配置一下环境变量. 接着,下载并安装Android SDK.下载过 ...

  2. 无限极分类sql数据库的设计

    --创建测试数据表tb ) , pid ) , name )) ' , null , '广东省') ' , '广州市') ' , '深圳市') ' , '天河区') ' , '罗湖区') ' , '福 ...

  3. 无法解决 equal to 运算中 "Chinese_PRC_CI_AS" 和 "Chinese_PRC_90_CI_AI" 之间的排序规则冲突。的解决方法

    在SQL SERVICE的查询的时候遇到了“无法解决 equal to 运算中 "Chinese_PRC_CI_AS" 和 "Chinese_PRC_90_CI_AI&q ...

  4. GridView自带分页 1总页数 首页 下一页 上一页 尾页 X 页 go 实现方法 .

    在前台GRIDVIEW中添加如下代码 <PagerTemplate> <table> <tr> <td style="text-align: rig ...

  5. BeanDefinition的Resource定位

    1.以编程的方式使用DefaultListableBeanFactory时,首先定义一个Resource来定位容器使用的BeanDefiniton.这时使用的是ClassPathResource,这意 ...

  6. (转)MongoDB 3.0 WT引擎参考配置文件

    mongodb 3.0 改变很多,从2.6版本升级到3.0要关注的细节很多,如权限等等.3.0在数据存储引擎上更换成了wiredTiger,在数据压缩方面很有效,解决大数据量问题的情况下,磁盘不够用的 ...

  7. 使用单用户模式破解Linux密码

    使用单用户模式破解Linux密码 特别说明:在实际工作应用中,安装Linux操作系统必须设置装载口令,否则很容易被破解. 1.使用reboot指令重启Linux操作系统 2.在进入操作系统数秒时,单击 ...

  8. 济南学习 Day 4 T1 pm

    幸运数字(number)Time Limit:1000ms Memory Limit:64MB题目描述LYK 最近运气很差,例如在 NOIP 初赛中仅仅考了 90 分,刚刚卡进复赛,于是它决定使用一些 ...

  9. C语言 猜数游戏--产生一个随机数

    #include <stdio.h> #include <time.h> #include <stdlib.h> int main(int argc, const ...

  10. 【风马一族_Android】Android Studio 给APP设置签名

    在Android Studio中,给App签名,如果没有给App设置签名的话,Android Studio会主动给app设置一个默认的签名 接下来,介绍主动给App设置一个签名的整个步骤过程: 1) ...