0
点赞
收藏
分享

微信扫一扫

SQL分页优化一 order by分页

分页框架

错误的分页框架:

select * 
 from (select t.*, rownum rn from (需要分页的SQL) t) 
where rn >= 1 
  and rn <= 10;

正确的分页框架:

select * 
 from (select * 
         from (select a.*, rownum rn from (需要分页的SQL) a) 
        where rownum <= 10) 
where rn >= 1;

测试

SQL> create table test as select * from dba_objects;
Table created.

SQL> select *
  2    from (select t.*, rownum rn
  3            from (select * from test order by object_id) t)
  4   where rn >= 1
  5     and rn <= 10;

10 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 3603170480

----------------------------------------------------------------------------------------
| Id  | Operation             | Name   | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |        | 75798 |    15M|       |  3702   (1)| 00:00:45 |
|*  1 |  VIEW                 |        | 75798 |    15M|       |  3702   (1)| 00:00:45 |
|   2 |   COUNT               |        |       |       |       |            |          |
|   3 |    VIEW               |        | 75798 |    14M|       |  3702   (1)| 00:00:45 |
|   4 |     SORT ORDER BY     |        | 75798 |    14M|    17M|  3702   (1)| 00:00:45 |
|   5 |      TABLE ACCESS FULL| TEST   | 75798 |    14M|       |   290   (1)| 00:00:04 |
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("RN"<=10 AND "RN">=1)

Note
-----
   - dynamic sampling used for this statement (level=2)

可以看到,该SQL在排序后取10行数据,SQL走全表扫。
创建索引:

SQL> create index idx_test on test(object_id,0);
Index created.

SQL强制走索引:

SQL>  select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  dbcvrrsh6aw2y, child number 0
-------------------------------------
select /*+gather_plan_statistics*/  *   from (select t.*, rownum rn
      from (select /*+index(test idx_test)*/                  *
            from test                  order by object_id) t)  where
rn >= 1    and rn <= 10

Plan hash value: 3119682446

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

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |          |      1 |        |     10 |00:00:00.01 |    1288 |
|*  1 |  VIEW                          |          |      1 |  75798 |     10 |00:00:00.01 |    1288 |
|   2 |   COUNT                        |          |      1 |        |  72571 |00:00:00.03 |    1288 |
|   3 |    VIEW                        |          |      1 |  75798 |  72571 |00:00:00.02 |    1288 |
|   4 |     TABLE ACCESS BY INDEX ROWID| test     |      1 |  75798 |  72571 |00:00:00.02 |    1288 |
|   5 |      INDEX FULL SCAN           | IDX_TEST |      1 |  75798 |  72571 |00:00:00.01 |     183 |
-----------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------

   1 - filter(("RN"<=10 AND "RN">=1))

SQL强制走索引,索引全扫,扫描全部数据,逻辑读1288。
索引本身具有排序功能,应该是只扫描部分叶子块就获取我们想要的数据。
代入正确的分页框架:

select *
  from (select *
          from (select a.*, rownum rn
                  from (select /*+index(test idx_test)*/
                         *
                          from test
                         order by object_id) a)
         where rownum <= 10)
 where rn >= 1;

SQL>  select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  95puvbgwqw5mh, child number 0
-------------------------------------
select *   from (select *           from (select a.*, rownum rn
          from (select /*+index(test idx_test)*/
    *                           from test
order by object_id) a)          where rownum <= 10)  where rn >= 1

Plan hash value: 1201925926

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

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |          |      1 |        |     10 |00:00:00.01 |       5 |
|*  1 |  VIEW                            |          |      1 |     10 |     10 |00:00:00.01 |       5 |
|*  2 |   COUNT STOPKEY                  |          |      1 |        |     10 |00:00:00.01 |       5 |
|   3 |    VIEW                          |          |      1 |  75798 |     10 |00:00:00.01 |       5 |
|   4 |     COUNT                        |          |      1 |        |     10 |00:00:00.01 |       5 |
|   5 |      VIEW                        |          |      1 |  75798 |     10 |00:00:00.01 |       5 |
|   6 |       TABLE ACCESS BY INDEX ROWID| test     |      1 |  75798 |     10 |00:00:00.01 |       5 |
|   7 |        INDEX FULL SCAN           | IDX_test |      1 |  75798 |     10 |00:00:00.01 |       3 |
-------------------------------------------------------------------------------------------------------


PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("RN">=1)
   2 - filter(ROWNUM<=10)

SQL走索引全扫,但是只扫描了10行数据,逻辑读为5;利用count stopkey特性,获取到分页语句需要的数据,SQL立即停止运行

优化思路

分页语句的优化思路:
如果分页语句中有排序(order by),要利用索引已经排序特性,将order by的列包含在索引中,同时利用rownum的count stopkey特性来优化分页SQL。
如果分页中没有排序,可以直接利用rownum的COUNT STOPKEY特性来优化分页SQL。

举报

相关推荐

0 条评论