zoukankan      html  css  js  c++  java
  • oracle优化-分页查询新认识

    在写这篇文章之前,对分页查询的认识就是经典的三层嵌套:第①层:获得需要的数据,第②层:用rownum限制展示数据的上限,第③层:用rownum的别名rn限制展示数据的下限。

    在生产中遇见一个两层嵌套满足分页查询的SQL,兴奋之余拿出来与大家分享。分三步走1:创建测试表;2:经典分页查询展示SQL及执行计划;3:两层嵌套分页查询展示SQL及执行计划

    1、创建测试表

    SQL>CREATE TABLE TEST_A(ID NUMBER NOT NULL,SYSID NUMBER,STATUS NUMBER,INFO VARCHAR2(2000));
    SQL>INSERT INTO TEST_A
    SELECT ROWNUM,ROWNUM+1,TRUNC(DBMS_RANDOM.VALUE(1,9)) ,RPAD('*',2000,'*') FROM DUAL CONNECT BY ROWNUM<=100000;
    SQL> ALTER table test_a add primary key(id);
    SQL>create index ind_status_sysid on test_a(status,sysid);

    说明:TEST_A中ID是主键,status字段取值[1,8]且均匀分布。
    要求:取TEST_A中status=5的记录,按sysid排序,展示1-1000行。

    2、经典分页查询展示SQL及执行计划

    SQL> select /*+gather_plan_statistics*/* from 
            (select rownum rn ,t.* from 
                (select * from test_a  where status=5 order by sysid )t
            where rownum<=1000) 
        where rn >=1;

    分析:
    第①层获得status=5且按sysid排序的数据
    第②层取返回数据的上限:rownum<=1000
    第③层取返回数据的下限:rn>=1

     查看SQL的执行计划

    SQL> select * from table(dbms_xplan.display_cursor('','','allstats last'));
    
    PLAN_TABLE_OUTPUT
    SQL_ID    2dtgu07a19fhk, child number 0
    -------------------------------------
    select /*+gather_plan_statistics*/* from (select rownum rn ,t.* from
    (select t.id,t.sysid,t.status from test_a t where status=5 order by
    sysid )t where rownum<=1000) where rn >=1
    
    Plan hash value: 503264694
    
    -------------------------------------------------------------------------------------------------------------
    | Id  | Operation                       | Name             | Starts   | E-Rows  | A-Rows |     A-Time   | Buffers |
    -------------------------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT                |                  |     1    |         |    1000 |00:00:00.01 |    957 |
    |*  1 |  VIEW                           |                  |     1    |   1000  |    1000 |00:00:00.01 |    957 |
    |*  2 |   COUNT STOPKEY                 |                  |     1    |         |    1000 |00:00:00.01 |    957 |
    |   3 |    VIEW                         |                  |     1    |  11186  |    1000 |00:00:00.01 |    957 |
    |   4 |     TABLE ACCESS BY INDEX ROWID | TEST_A           |     1    |  11186  |    1000 |00:00:00.01 |    957 |
    |*  5 |      INDEX RANGE SCAN           | IND_STATUS_SYSID |     1    |         |    1000 |00:00:00.01 |     71 |
    -------------------------------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
       1 - filter("RN">=1)
       2 - filter(ROWNUM<=1000)
       5 - access("STATUS"=5)
    
    Note
    -----
       - dynamic statistics used: dynamic sampling (level=2)
    
    
    30 rows selected.
    
    Elapsed: 00:00:00.04

    分析:
    Oracle满足条件的数据有1W多条,但是Oracle只取了1000条数据,从A-Rows中可以看出,大大提高了SQL的性能。
    因为Oracle在执行SQL的时候会将查询条件推入到内层查询中,查到满足条件的1000条记录,就会返回,在执行计划COUNT STOPKEY上展示出来。

    3:两层嵌套分页查询展示SQL及执行计划

    SQL> select /*+gather_plan_statistics*/* from (select row_number()over(order by sysid) rn,t.* from test_a t where status=5) where rn between 1 and 1000;
    
    SQL> select * from table(dbms_xplan.display_cursor('','','allstats last'));
    
    PLAN_TABLE_OUTPUT
    
    SQL_ID    cj0ds74naryn3, child number 0
    -------------------------------------
    select /*+gather_plan_statistics*/* from (select row_number()over(order
    by sysid) rn,t.* from test_a t where status=5) where rn between 1 and
    1000
    
    Plan hash value: 1970407823
    
    ------------------------------------------------------------------------------------------------------------
    | Id  | Operation                       | Name             | Starts | E-Rows | A-Rows | A-Time     | Buffers |
    ------------------------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT                |                  |    1   |        |   1000 |00:00:00.01 |     957 |
    |*  1 |  VIEW                           |                  |    1   |   1000 |   1000 |00:00:00.01 |     957 |
    |*  2 |   WINDOW NOSORT STOPKEY         |                  |    1   |  11186 |   1000 |00:00:00.01 |     957 |
    |   3 |    TABLE ACCESS BY INDEX ROWID  | TEST_A           |    1   |  11186 |   1000 |00:00:00.01 |     957 |
    |*  4 |     INDEX RANGE SCAN            | IND_STATUS_SYSID |    1   |        |   1000 |00:00:00.01 |      71 |
    ------------------------------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
        1 - filter(("RN">=1 AND "RN"<=1000))
        2 - filter(ROW_NUMBER() OVER ( ORDER BY "SYSID")<=1000)
        4 - access("STATUS"=5)
    
    Note
    -----
       - dynamic statistics used: dynamic sampling (level=2)
    
    
    29 rows selected.
    
    Elapsed: 00:00:00.03

    分析:两层嵌套也达到了,查到满足1000条记录就返回的要求,逻辑读的消耗同样是957。
    以上两种分页查询同时满足了取1000条符合条件的流水的目的,且效率高,可直接拿来用在生产中。

  • 相关阅读:
    html+css学习笔记 5[表格、表单]
    html+css学习笔记 4[定位]
    WebService基于SoapHeader实现安全认证
    jquery获取url参数
    邮件群发
    .NET指定程序集的位置
    C# 只启动一个实例完全解决方案
    利用Google API生成二维码
    redis 面试题
    python 操作 redis 集群
  • 原文地址:https://www.cnblogs.com/handhead/p/13856505.html
Copyright © 2011-2022 走看看