Oracle分页查询优化

  • 格式:doc
  • 大小:165.50 KB
  • 文档页数:49

Oracle的分页查询语句基本上可以按照本文给出的格式来进行套用。

Oracle分页查询语句(一)分页查询格式:SELECT * FROM(SELECT A.*, ROWNUM RNFROM (SELECT * FROM TABLE_NAME) AWHERE ROWNUM <= 40)WHERE RN >= 21其中最内层的查询SELECT * FROM TABLE_NAME表示不进行翻页的原始查询语句。

ROWNUM <= 40和RN >= 21控制分页查询的每页的范围。

上面给出的这个分页查询语句,在大多数情况拥有较高的效率。

分页的目的就是控制输出结果集大小,将结果尽快的返回。

在上面的分页查询语句中,这种考虑主要体现在WHERE ROWNUM <= 40这句上。

选择第21 到40条记录存在两种方法,一种是上面例子中展示的在查询的第二层通过ROWNUM <= 40来控制最大值,在查询的最外层控制最小值。

而另一种方式是去掉查询第二层的WHERE ROWNUM <= 40语句,在查询的最外层控制分页的最小值和最大值。

这是,查询语句如下:SELECT * FROM(SELECT A.*, ROWNUM RNFROM (SELECT * FROM TABLE_NAME) A)WHERE RN BETWEEN 21 AND 40对比这两种写法,绝大多数的情况下,第一个查询的效率比第二个高得多。

这是由于CBO 优化模式下,Oracle可以将外层的查询条件推到内层查询中,以提高内层查询的执行效率。

对于第一个查询语句,第二层的查询条件WHERE ROWNUM <= 40就可以被Oracle推入到内层查询中,这样Oracle查询的结果一旦超过了ROWNUM限制条件,就终止查询将结果返回了。

而第二个查询语句,由于查询条件BETWEEN 21 AND 40是存在于查询的第三层,而Oracle无法将第三层的查询条件推到最内层(即使推到最内层也没有意义,因为最内层查询不知道RN代表什么)。

因此,对于第二个查询语句,Oracle最内层返回给中间层的是所有满足条件的数据,而中间层返回给最外层的也是所有数据。

数据的过滤在最外层完成,显然这个效率要比第一个查询低得多。

上面分析的查询不仅仅是针对单表的简单查询,对于最内层查询是复杂的多表联合查询或最内层查询包含排序的情况一样有效。

这里就不对包含排序的查询进行说明了,下一篇文章会通过例子来详细说明。

下面简单讨论一下多表联合的情况。

对于最常见的等值表连接查询,CBO 一般可能会采用两种连接方式NESTED LOOP和HASH JOIN (MERGE JOIN效率比HASH JOIN效率低,一般CBO不会考虑)。

在这里,由于使用了分页,因此指定了一个返回的最大记录数,NESTED LOOP在返回记录数超过最大值时可以马上停止并将结果返回给中间层,而HASH JOIN必须处理完所有结果集(MERGE JOIN也是)。

那么在大部分的情况下,对于分页查询选择NESTED LOOP作为查询的连接方法具有较高的效率(分页查询的时候绝大部分的情况是查询前几页的数据,越靠后面的页数访问几率越小)。

因此,如果不介意在系统中使用HINT的话,可以将分页的查询语句改写为:SELECT /*+ FIRST_ROWS */ * FROMSELECT A.*, ROWNUM RNFROM (SELECT * FROM TABLE_NAME) AWHERE ROWNUM <= 40)WHERE RN >= 21Oracle分页查询语句(二)这篇文章用几个例子来说明分页查询的效率。

首先构造一个比较大的表作为测试表:SQL> CREATE TABLE T AS SELECT * FROM DBA_OBJECTS, DBA_SEQUENCES;表已创建。

SQL> SELECT COUNT(*) FROM T;COUNT(*)----------457992首先比较两种分页方法的区别:SQL> SET AUTOT ONSQL> COL OBJECT_NAME FORMA T A30SQL> EXEC DBMS_STATS.GATHER_TABLE_STA TS(USER, 'T')PL/SQL 过程已成功完成。

SQL> SELECT OBJECT_ID, OBJECT_NAME2 FROM3 (4 SELECT ROWNUM RN, OBJECT_ID, OBJECT_NAME5 FROM6 (7 SELECT OBJECT_ID, OBJECT_NAME FROM T8 )9 )10 WHERE RN BETWEEN 11 AND 20;OBJECT_ID OBJECT_NAME---------- ------------------------------5807 ALL_APPLY_PROGRESS1769 ALL_ARGUMENTS2085 ALL_ASSOCIATIONS4997 ALL_AUDIT_POLICIES4005 ALL_BASE_TABLE_MVIEWS5753 ALL_CAPTURE5757 ALL_CAPTURE_PARAMETERS5761 ALL_CAPTURE_PREPARED_DA TABASE5765 ALL_CAPTURE_PREPARED_SCHEMAS5769 ALL_CAPTURE_PREPARED_TABLES已选择10行。

Execution Plan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=864 Card=457992 Bytes=42135264)1 0 VIEW (Cost=864 Card=457992 Bytes=42135264)2 1 COUNT3 2 TABLE ACCESS (FULL) OF 'T' (Cost=864 Card=457992 Bytes=9617832)Statistics----------------------------------------------------------0 recursive calls0 db block gets8979 consistent gets7422 physical reads0 redo size758 bytes sent via SQL*Net to client503 bytes received via SQL*Net from client2 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)10 rows processedSQL> SELECT OBJECT_ID, OBJECT_NAME2 FROM3 (4 SELECT ROWNUM RN, OBJECT_ID, OBJECT_NAME5 FROM6 (7 SELECT OBJECT_ID, OBJECT_NAME FROM T8 )9 WHERE ROWNUM <= 2010 )11 WHERE RN >= 11;OBJECT_ID OBJECT_NAME---------- ------------------------------5807 ALL_APPLY_PROGRESS1769 ALL_ARGUMENTS2085 ALL_ASSOCIATIONS4997 ALL_AUDIT_POLICIES4005 ALL_BASE_TABLE_MVIEWS5753 ALL_CAPTURE5757 ALL_CAPTURE_PARAMETERS5761 ALL_CAPTURE_PREPARED_DA TABASE5765 ALL_CAPTURE_PREPARED_SCHEMAS5769 ALL_CAPTURE_PREPARED_TABLES已选择10行。

Execution Plan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=864 Card=20 Bytes=1840)1 0 VIEW (Cost=864 Card=20 Bytes=1840)2 1 COUNT (STOPKEY)3 2 TABLE ACCESS (FULL) OF 'T' (Cost=864 Card=457992 Bytes=9617832)Statistics----------------------------------------------------------0 recursive calls0 db block gets5 consistent gets0 physical reads0 redo size758 bytes sent via SQL*Net to client503 bytes received via SQL*Net from client2 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)10 rows processed二者执行效率相差很大,一个需要8000 多逻辑读,而另一个只需要5个逻辑读。

观察二者的执行计划可以发现,两个执行计划唯一的区别就是第二个查询在COUNT这步使用了STOPKEY,也就是说,Oracle 将ROWNUM <= 20推入到查询内层,当符合查询的条件的记录达到STOPKEY的值,则Oracle结束查询。

因此,可以预见,采用第二种方式,在翻页的开始部分查询速度很快,越到后面,效率越低,当翻到最后一页,效率应该和第一种方式接近。

SQL> SELECT OBJECT_ID, OBJECT_NAME2 FROM3 (4 SELECT ROWNUM RN, OBJECT_ID, OBJECT_NAME5 FROM6 (7 SELECT OBJECT_ID, OBJECT_NAME FROM T8 )9 WHERE ROWNUM <= 45799010 )11 WHERE RN >= 457980;OBJECT_ID OBJECT_NAME---------- ------------------------------7128 XCF_I_HANDLE_STA TUS7126 XCF_P7127 XCF_U17142 XDF7145 XDF_I_DF_KEY7146 XDF_I_HANDLE_STA TUS7143 XDF_P7144 XDF_U1TEST.Y ANGTINGKUNTEST4.Y ANGTINGKUNY ANGTK.Y ANGTINGKUN已选择11行。