首页 > 数据库 >SQL分页优化四 等值+非等值+order by分页

SQL分页优化四 等值+非等值+order by分页

时间:2023-08-05 16:01:46浏览次数:36  
标签:00 等值 name object order owner id select 分页

测试验证

现有如下SQL,每页显示10条: 
select * 
 from test 
where owner = 'SYS' 
  and object_id > 1000 
order by object_name; 

select * 
 from (select * 
         from (select a.*, rownum rn 
                 from (select * 
                         from test 
                        where owner = 'SYS' 
                          and object_id > 1000 
                        order by object_name) a) 
        where rownum <= 10) 
where rn >= 1;

分页语句中有排序列,创建索引的时候,需要将排序列包含在索引中。所以只需要将过滤列owner、object_id以及排序列object_name组合起来创建索引。
因为owner是等值过滤,object_id列是非等值过滤,创建索引时,优先将等值过滤和排序列组合起来,然后再将非等值过滤列放在后面。

SQL> create index idx_owner_name_id on test(owner,object_name,object_id);

Index created.

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


PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  6n5yu1h14fdrh, child number 0
-------------------------------------
select *   from (select *           from (select a.*, rownum rn
          from (select /*+index(test idx_owner_name_id)*/
           *                           from test
    where owner = 'SYS'                            and object_id > 1000
                         order by object_name) a)          where rownum
<= 10)  where rn >= 1

Plan hash value: 2090516350

--------------------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |                   |      1 |        |     10 |00:00:00.01 |      15 |
|*  1 |  VIEW                            |                   |      1 |     10 |     10 |00:00:00.01 |      15 |
|*  2 |   COUNT STOPKEY                  |                   |      1 |        |     10 |00:00:00.01 |      15 |
|   3 |    VIEW                          |                   |      1 |  31163 |     10 |00:00:00.01 |      15 |
|   4 |     COUNT                        |                   |      1 |        |     10 |00:00:00.01 |      15 |
|   5 |      VIEW                        |                   |      1 |  31163 |     10 |00:00:00.01 |      15 |
|   6 |       TABLE ACCESS BY INDEX ROWID| TEST              |      1 |  31163 |     10 |00:00:00.01 |      15 |
|*  7 |        INDEX RANGE SCAN          | IDX_OWNER_NAME_ID |      1 |  31163 |     10 |00:00:00.01 |       5 |
--------------------------------------------------------------------------------------------------------------

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

   1 - filter("RN">=1)
   2 - filter(ROWNUM<=10)
   7 - access("OWNER"='SYS' AND "OBJECT_ID">1000)
       filter("OBJECT_ID">1000)

从执行计划中看到,没有sort order by。逻辑读也才15个,说明执行计划非常理想。

尝试创建以下索引:

SQL> create index idx_owner_id_name on test(owner,object_id,object_name);

Index created.

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

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  4ypy16hfrrfv5, child number 0
-------------------------------------
select *   from (select *           from (select a.*, rownum rn
          from (select /*+index(test idx_owner_id_name)*/
           *                           from test
    where owner = 'SYS'                            and object_id > 1000
                         order by object_name) a)          where rownum
<= 10)  where rn >= 1

Plan hash value: 2498002320

---------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                         | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  OMem |  1Mem | Used-Mem |
---------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |                   |      1 |        |     10 |00:00:00.04 |    1003 |    191 |       |       |          |
|*  1 |  VIEW                             |                   |      1 |     10 |     10 |00:00:00.04 |    1003 |    191 |       |       |          |
|*  2 |   COUNT STOPKEY                   |                   |      1 |        |     10 |00:00:00.04 |    1003 |    191 |       |       |          |
|   3 |    VIEW                           |                   |      1 |  31163 |     10 |00:00:00.04 |    1003 |    191 |       |       |          |
|   4 |     COUNT                         |                   |      1 |        |     10 |00:00:00.04 |    1003 |    191 |       |       |          |
|   5 |      VIEW                         |                   |      1 |  31163 |     10 |00:00:00.04 |    1003 |    191 |       |       |          |
|   6 |       SORT ORDER BY               |                   |      1 |  31163 |     10 |00:00:00.04 |    1003 |    191 |  4376K|   884K| 3889K (0)|
|   7 |        TABLE ACCESS BY INDEX ROWID| TEST              |      1 |  31163 |  29945 |00:00:00.03 |    1003 |    191 |       |       |          |
|*  8 |         INDEX RANGE SCAN          | IDX_OWNER_ID_NAME |      1 |  31163 |  29945 |00:00:00.02 |     189 |    191 |       |       |          |
---------------------------------------------------------------------------------------------------------------------------------------------------

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

   1 - filter("RN">=1)
   2 - filter(ROWNUM<=10)
   8 - access("OWNER"='SYS' AND "OBJECT_ID">1000 AND "OBJECT_ID" IS NOT NULL)

执行计划中有sort order by,说明没有用到索引已经排序特性,而且逻辑读为1003个,说明执行计划是错误的。
这是因为该分页语句是根据object_name进行排序的,在创建索引的时候是按照owner,object_id,object_name顺序创建索引的。
按照索引排序的方式从表中取出5条数据观察:

SQL> select *
  2    from (select rownum rn, owner, object_id, object_name
  3            from test
  4           where owner = 'SYS'
  5             and object_id > 1000
  6           order by owner, object_id, object_name)
  7   where rownum <= 5;

        RN OWNER  OBJECT_ID OBJECT_NAME
---------- ----- ---------- ---------------
         1 SYS         1001 NOEXP$
         2 SYS         1002 EXPPKGOBJ$
         3 SYS         1003 I_OBJTYPE
         4 SYS         1004 EXPPKGACT$
         5 SYS         1005 I_ACTPACKAGE

在这前5条数据中,我们按照分页语句排序条件object_name进行排序,应该是第4行数据显示为第一行数据,但是它在索引中排到了第4行,所以索引中数据的顺序并不能满足分页语句中的排序要求,这就产生了sort order by,进而导致执行计划出错。

按照正确的owner,object_name,object_id顺序创建索引时,索引中前5条数据:

SQL> select *   
  2    from (select rownum rn, owner, object_id, object_name
  3            from test
  4           where owner = 'SYS'
  5             and object_id > 1000
  6           order by owner, object_name, object_id)
  7   where rownum <= 5;

        RN OWNER  OBJECT_ID OBJECT_NAME
---------- ----- ---------- ----------------------------------------
         1 SYS        34042 /1000323d_DelegateInvocationHa
         2 SYS        44844 /1000e8d1_LinkedHashMapValueIt
         3 SYS        23397 /1005bd30_LnkdConstant
         4 SYS        19737 /10076b23_OraCustomDatumClosur
         5 SYS        45460 /100c1606_StandardMidiFileRead

索引中的数据顺序完全符合分页语句中的排序要求,这就不需要进行sort order by,所以执行计划是正确的。

标签:00,等值,name,object,order,owner,id,select,分页
From: https://blog.51cto.com/u_13482808/6976294

相关文章

  • SQL分页优化五 like+非等值+order by分页
    测试验证如下SQL:select*from(select*from(selecta.*,rownumrnfrom(select*fromtestwhereownerlike'SYS%'......
  • MySQL查询排序和分页
    连接数据库mysql-hlocalhost-uroot-proot排序查询语法:select字段列表from表名orderby字段1排序方式1,字段3排序方式2,字段3排序方式3,....;ASC:升序(默认值)DESC降序注意:如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序。根据年龄对公司......
  • web分页查询测试
    查询单条件查询输入框测试输入文本文本数据格式检查文本前面带空格文本中间带空格文本结尾带空格特殊字符&emoji查询清空查询条件返回结果检查各字段值检查返回的结果为空前端展示组合条件查询重置功能安全测试用户权限校验重要数据脱敏防止SQL注入......
  • 菜品分页查询_代码开发和功能测试
           ......
  • 菜品分页查询_需求分析和设计
       ......
  • 前端分页和搜索
    需求一般来说,分页和搜索都是后端处理的。但有时候后端没处理,就只能前端处理了。当然这要在数据量不大的情况下,否则会性能消耗很大。分析使用setTimeout(()=>{},1000)模拟接口调用;数据总条数total是符合搜索结果的数据总条数;使用分页组件,搭配arr.slice()返回当前页......
  • 使用JPA实现分页和模糊查询,以及jpa分页工具类
     1、首先创建DAO层接口,实现JpaRepository和JpaSpecificationExecutor两个接口JpaRepository<SysOperLog,Integer> 泛型参数分别是:要查询的实体(Entity),实体的主键类型JpaSpecificationExecutor<SysOperLog> 泛型参数:要查的实体 @RepositorypublicinterfaceSysOperLogReposi......
  • jpa 分页工具类,把 list 转 page 的工具类
    JpaPageUtils.java工具类importjava.util.ArrayList;importjava.util.List;importjava.util.Optional;importorg.springframework.data.domain.Page;importorg.springframework.data.domain.PageImpl;importorg.springframework.data.domain.PageRequest;importo......
  • @Order 一个使用场景
    @order注解是spring-core包下的一个注解,@Order的作用是定义SpringIOC容器中Bean的执行顺序的优先级(这里的顺序也可以理解为存放到容器中的先后顺序)。遇到过使用@Order的场景:某个利用Redisincr自增编码,服务迁移,Redis数据没有维护好,导致自增的编码丢失,下一次自增又会从头开始,这......
  • 千万条据下的分页
        1.1. 背景对于开发来说,分页功能碰到的频率还是算蛮高的,基本上在每个模块中都需要都会遇到列表分页的功能。他们实现的都很快,因为基本上只要把之前的代码改改就OK了。他们的实现基本是是如下语句: SELECT*FROMgoodsWHERE......