2017 年,网络优化班的学生问怎么优化以下语句。
select rownum as r, a.*
from (select npai.AREA_ID,
npai.PSO_ID,
npai.RO_ID,
npai.NO,
npai.ADDR,
to_char(npai.CRTD_DT, 'yyyy-mm-dd hH24:mi:ss') as CRTD_DT,
to_char(npai.CMPLT_DT, 'yyyy-mm-dd hH24:mi:ss') as CMPLT_DT,
npai.CRM_PROD_ID,
npai.PROD_SERV_SPEC_ID,
npai.PROD_SERV_SPEC_NAME,
npai.ACTION_TP_ID,
npai.ACTION_TP_NAME
from NT_PSO_ARCH_INFO npai
where npai.crtd_dt >= to_date('2017-01-01', 'yyyy-mm-dd')
and npai.crtd_dt <= to_date('2017-02-01', 'yyyy-mm-dd')
and local_area_id = 3
order by crtd_dt) a
where rownum <= 20;
执行计划如下。
Plan hash value: 2467293374
-------------------------------------------------------------------------------------
| Id |Operation |Name |Rows |Bytes|Cost(%CPU)|
-------------------------------------------------------------------------------------
| 0 |SELECT STATEMENT | | 20|28160| 489K (1)|
|* 1 | COUNT STOPKEY | | | | |
| 2 | VIEW | | 950K|1276M| 489K (1)|
|* 3 | SORT ORDER BY STOPKEY | | 950K| 85M| 489K (1)|
| 4 | PARTITION LIST SINGLE | | 950K| 85M| 469K (1)|
| 5 | TABLE ACCESS BY LOCAL INDEX ROWID|NT_PSO_ARCH_INFO| 950K| 85M| 469K (1)|
|* 6 | INDEX RANGE SCAN |IDX_NTPAI_CRDT | 950K| | 2581 (1)|
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=20)
3 - filter(ROWNUM<=20)
6 - access("NPAI"."CRTD_DT">=TO_DATE(' 2017-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss')
AND "NPAI"."CRTD_DT"<=TO_DATE('2017-02-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
该 SQL 类似分页语句。拿到分页语句,我们应该先查看分页语句是否符合分页编码规范。这里,SQL 完全符合分页语句编码规范。
该 SQL 排序列是 crtd_dt,执行计划中走的也是 crtd_dt 列的索引。表 nt_pso_arch_info 是 LIST 分区表,分区列是 local_area_id,从执行计划中(Id=4)看到只扫描了一个分区。按道理该 SQL 不应该出现 SORT ORDER BY。为什么执行计划中有 SORT ORDER BY 呢?我们注意观察,SQL 语句中 order by 的列 crtd_dt 在 select 中进行了 to_char 格式化,格式化之后取了别名,但是别名居然与列名一样。正是因为别名与列名一样,才导致无法消除 SORT ORDER BY。
现在我们另外取一个别名(CRTD_DT1)。
select rownum as r, a.*
from (select npai.AREA_ID,
npai.PSO_ID,
npai.RO_ID,
npai.NO,
npai.ADDR,
to_char(npai.CRTD_DT, 'yyyy-mm-dd hH24:mi:ss') as CRTD_DT1,
to_char(npai.CMPLT_DT, 'yyyy-mm-dd hH24:mi:ss') as CMPLT_DT,
npai.CRM_PROD_ID,
npai.PROD_SERV_SPEC_ID,
npai.PROD_SERV_SPEC_NAME,
npai.ACTION_TP_ID,
npai.ACTION_TP_NAME
from NT_PSO_ARCH_INFO npai
where npai.crtd_dt >= to_date('2017-01-01', 'yyyy-mm-dd')
and npai.crtd_dt <= to_date('2017-02-01', 'yyyy-mm-dd')
and local_area_id = 3
order by crtd_dt) a
where rownum <= 20;
我们再次查看执行计划。
Plan hash value: 3066843972
-------------------------------------------------------------------------------------
| Id |Operation |Name |Rows |Bytes| Cost(%CPU)|
-------------------------------------------------------------------------------------
| 0 |SELECT STATEMENT | | 20|28160| 489K (1)|
|* 1 | COUNT STOPKEY | | | | |
| 2 | VIEW | | 950K|1276M| 489K (1)|
| 3 | PARTITION LIST SINGLE | | 950K| 85M| 469K (1)|
| 4 | TABLE ACCESS BY LOCAL INDEX ROWID|NT_PSO_ARCH_INFO| 950K| 85M| 469K (1)|
|* 5 | INDEX RANGE SCAN |IDX_NTPAI_CRDT 950K| | 2581 (1)|
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=20)
5 - access("NPAI"."CRTD_DT">=TO_DATE(' 2017-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
"NPAI"."CRTD_DT"<=TO_DATE(' 2017-02-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
更改别名之后,消除了 SORT ORDER BY,从而达到了优化目的。为什么必须要更改别名呢?这是因为如果不更改别名,order by crtd_dt 就相当于 order by 别名,也就是 order by to_char (npai.CRTD_DT, ‘yyyy-mm-dd hH24:mi:ss’),而索引中记录的是 date 类型,现在排序变成了按照 char 类型排序,如果不更改别名执行计划就无法消除 SORT ORDER BY。