当前位置: 首页 > 调优 > 正文

下面测试来至于于一个网友的聊天,关于分页语句。下面在自己的平台简单的测试一把。判断分页语句是否最优化,其实可以简单的看执行计划是否包含SORT ORDER BY STOPKEY这一列,如果有这一列,意味着查询完所有的数据,在对数据进行排序的时,根据rownum的值来终止排序过程。分页语句其实不仅仅要实现功能上的分页功能,也要使用利用rownum来终止对数据的访问。

1,环境介绍

www.htz.pw > host uname -a

windows32 luoping 2.6.1 7601 i686-pc Intel unknown MinGW

 

www.htz.pw > select * from v$version;

 

BANNER

——————————————————————————–

Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 – 64bit Production

PL/SQL Release 11.2.0.3.0 – Production

CORE    11.2.0.3.0      Production

TNS for 64-bit Windows: Version 11.2.0.3.0 – Production

NLSRTL Version 11.2.0.3.0 – Production

2,创建测试数据

利用dba_objects表来创建测试表

www.htz.pw > create user htz identified by oracle;

User created.

www.htz.pw > grant dba to htz;

Grant succeeded.

www.htz.pw > create table htz.page1 as select * from dba_objects where rownum<20;

Table created.

www.htz.pw > create table htz.pages2 as select * from dba_objects;

Table created.

www.htz.pw > create index htz.ind_pages2_1 on htz.pages2(object_id,owner);

Index created.

3,配置参数

请注意是在会话级别修改参数为all,用于收集执行计划每一步实际还回的行数。也可以通过增加提示来实现。在优化OLTP环境SQL时候,如果不知道执行计划中那一步消耗大量的资源,也可以通过配置此参数,来收集详细的信息。

www.htz.pw > alter session set statistics_level=all;

 

Session altered.

4,分页语句执行

这里按a.object_id排序,取第5行到第10行之间的数据

4.1 只实现分页功能

select owner, object_id, object_type, object_name, rn

  from (select owner, object_id, object_type, object_name, rownum rn

          from (select /*+ use_nl(a b)*/

                 a.owner, a.object_id, a.object_type, b.object_name

                  from htz.page1 a, htz.pages2 b

                 where a.owner = b.owner

                   and b.object_id > 50000

                 order by a.object_id)

         where rownum < 10)

 where rn > 5

 

SQL> @plan_by_last.sql

SQL> set echo off

Enter value for sqlid:

old   1: SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(nvl(‘&sqlid’,null),null,’ALLSTATS LAST’))

new   1: SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(nvl(”,null),null,’ALLSTATS LAST’))

Plan hash value: 1735455317

 

————————————————————————————————-

| Id  | Operation                        | Name         | Starts | E-Rows | A-Rows |   A-Time   |

————————————————————————————————-

|   0 | SELECT STATEMENT                 |              |      1 |        |      4 |00:00:00.13 |

|*  1 |  VIEW                            |              |      1 |      9 |      4 |00:00:00.13 |

|*  2 |   COUNT STOPKEY                  |              |      1 |        |      9 |00:00:00.13 |

|   3 |    VIEW                          |              |      1 |    208K|      9 |00:00:00.13 |

|*  4 |     SORT ORDER BY STOPKEY        |              |      1 |    208K|      9 |00:00:00.13 |

|   5 |      NESTED LOOPS                |              |      1 |        |  79933 |00:00:00.12 |

|   6 |       NESTED LOOPS               |              |      1 |    208K|  79933 |00:00:00.05 |

|   7 |        TABLE ACCESS FULL         | PAGE1        |      1 |     19 |     19 |00:00:00.01 |

|*  8 |        INDEX RANGE SCAN          | IND_PAGES2_1 |     19 |   1051 |  79933 |00:00:00.04 |

|   9 |       TABLE ACCESS BY INDEX ROWID| PAGES2       |  79933 |  10981 |  79933 |00:00:00.03 |

————————————————————————————————-

 

Predicate Information (identified by operation id):

—————————————————

 

   1 – filter("RN">5)

   2 – filter(ROWNUM<10)

   4 – filter(ROWNUM<10)

   8 – access("B"."OBJECT_ID">50000 AND "A"."OWNER"="B"."OWNER" AND "B"."OBJECT_ID" IS NOT NULL)

       filter("A"."OWNER"="B"."OWNER")

通过执行计划可以到,还回4条记录,但是访问了所有的数据。IND_PAGES2_1被执行了19次,等于PAGE1的总行数。PAGES2回表79933次。

4.2 利用rownum终止对数据的访问

利用rownum终止对数据的访问有几个前提条件:1order by后面的列,只能是驱动表中的列。2,执行计划要走NL的方式,3order by后面列所在的表为驱动表。4SQL语句中不能包括minus,union,union all等待关键字。5,驱动表在where中的所有列需要都在索引中,select不要求。

创建索引

www.htz.pw > create index htz.ind_page1_2 on htz.page1(object_id,owner);

 

Index created.

www.htz.pw > select owner, object_id, object_type, object_name, rn

  2    from (select owner, object_id, object_type, object_name, rownum rn

  3            from (select /*+ use_nl(a b) index(a ind_page1_2)*/

  4                   a.owner, a.object_id, a.object_type, b.object_name

  5                    from htz.page1 a, htz.pages2 b

  6                   where a.owner = b.owner

  7                     and b.object_id > 50000

  8                   order by a.object_id)

  9           where rownum < 10)

 10   where rn > 5

 11  ;

www.htz.pw > @plan_by_last.sql

www.htz.pw > set echo off

Enter value for sqlid:

old   1: SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(nvl(‘&sqlid’,null),null,’ALLSTATS LAST’))

new   1: SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(nvl(”,null),null,’ALLSTATS LAST’))

 

PLAN_TABLE_OUTPUT

————————————————————————————————————————–SQL_ID  1nvjmktk0rpqm, child number 0

————————————-

select owner, object_id, object_type, object_name, rn   from (select

owner, object_id, object_type, object_name, rownum rn           from

(select /*+ use_nl(a b) index(a ind_page1_2)*/

a.owner, a.object_id, a.object_type, b.object_name

from htz.page1 a, htz.pages2 b                  where a.owner = b.owner

                   and b.object_id > 50000                  order by

a.object_id)          where rownum < 10)  where rn > 5

 

Plan hash value: 2290584084

 

———————————————————————————————————–

| Id  | Operation                        | Name         | Starts | E-Rows | A-Rows |   A-Time   | Buffers |

———————————————————————————————————–

|   0 | SELECT STATEMENT                 |              |      1 |        |      4 |00:00:00.01 |    7 |

|*  1 |  VIEW                            |              |      1 |      9 |      4 |00:00:00.01 |    7 |

|*  2 |   COUNT STOPKEY                  |              |      1 |        |      9 |00:00:00.01 |    7 |

|   3 |    VIEW                          |              |      1 |  30014 |      9 |00:00:00.01 |    7 |

|   4 |     NESTED LOOPS                 |              |      1 |        |      9 |00:00:00.01 |    7 |

|   5 |      NESTED LOOPS                |              |      1 |  30014 |      9 |00:00:00.01 |    5 |

|   6 |       TABLE ACCESS BY INDEX ROWID| PAGE1        |      1 |     19 |      1 |00:00:00.01 |    2 |

|   7 |        INDEX FULL SCAN           | IND_PAGE1_2  |      1 |     19 |      1 |00:00:00.01 |    1 |

|*  8 |       INDEX RANGE SCAN           | IND_PAGES2_1 |      1 |      1 |      9 |00:00:00.01 |    3 |

|   9 |      TABLE ACCESS BY INDEX ROWID | PAGES2       |      9 |   1580 |      9 |00:00:00.01 |    2 |

———————————————————————————————————–

 

Predicate Information (identified by operation id):

—————————————————

 

   1 – filter("RN">5)

   2 – filter(ROWNUM<10)

   8 – access("B"."OBJECT_ID">50000 AND "A"."OWNER"="B"."OWNER" AND "B"."OBJECT_ID" IS NOT NULL)

       filter("A"."OWNER"="B"."OWNER")

 

Note

—–

   – dynamic sampling used for this statement (level=2)

 

 

39 rows selected.

这里已经没有看到SORT ORDER BY STOPKEY这一行,并且被驱动表执行次数为rownum指定的还回的行数。说明通过rownum已经实现终止对更多数据的访问。

需要注意的表连接方法走的是NL,如果rownum的取值大于驱动表还回的总行数的1/2的时候,我们需要更改一下SQL,可以走索引的desc。如果rownum1/2左右的时候,此方法的效率可能还没有走HASH的效率高。

分页语句的简单测试:等您坐沙发呢!

发表评论

gravatar

? razz sad evil ! smile oops grin eek shock ??? cool lol mad twisted roll wink idea arrow neutral cry mrgreen

快捷键:Ctrl+Enter