在数据库查询优化中,ORDER BY 子句导致的排序操作往往是性能瓶颈之一。本文将通过一个实际案例,展示如何通过合理的索引设计来消除排序操作,显著提升查询性能。
我们有一个销售表 ITPUX_SALES,包含以下字段:
logtime:交易时间cardid:卡号amount:交易金额业务需求:查询最近9年的销售记录,并按交易时间降序、卡号降序排列。
初始查询语句:
select
logtime,
cardid,
amount
from
ITPUX_SALES
where
logtime >= trunc(sysdate) - interval '9' year
order by
logtime desc,
cardid desc;
此时表中只有一个单列索引 IDX_SALE_TIME(仅包含 logtime 字段),执行计划显示:
1 NSET2:[9, 2681->2677, 85]
2 PRJT2:[9, 2681->2677, 85];exp_num(4), is_atom(FALSE)
3 SORT3:[9, 2681->2677, 85];key_num(2), partition_key_num(1), is_distinct(FALSE), is_adaptive(0), MEM_USED(30720KB), DISK_USED(0KB)
4 BLKUP2:[8, 2681->2677, 85];IDX_SALE_TIME(ITPUX_SALES)
5 SSEK2:[8, 2681->2677, 85];scan_type(DESC), IDX_SALE_TIME(ITPUX_SALES), is_global(0), scan_range[exp11-exp12,max]
关键发现:执行计划中出现了 SORT3 操作符,说明数据库在进行显式排序,使用了30MB内存。
我们创建包含两个排序字段的复合索引:
create index itpux_sales_log_card on ITPUX_SALES(logtime, cardid);
删除旧索引后重新执行查询,执行计划变为:
1 NSET2:[9, 2681->2677, 85]
2 PRJT2:[9, 2681->2677, 85];exp_num(4), is_atom(FALSE)
3 BLKUP2:[8, 2681->2677, 85];ITPUX_SALES_LOG_CARD(ITPUX_SALES)
4 SSEK2:[8, 2681->2677, 85];scan_type(DESC), ITPUX_SALES_LOG_CARD(ITPUX_SALES), is_global(0), scan_range[(exp11-exp12,min),(max,max))
优化效果:SORT3 操作符消失了!数据库通过反向扫描索引直接获得了正确排序的结果。
现在修改查询,要求按交易时间降序、卡号升序排列:
select
logtime,
cardid,
amount
from
ITPUX_SALES
where
logtime >= trunc(sysdate) - interval '9' year
order by
logtime desc,
cardid; -- cardid 改为升序
执行计划显示:
1 NSET2:[9, 2681->2677, 85]
2 PRJT2:[9, 2681->2677, 85];exp_num(4), is_atom(FALSE)
3 SORT3:[9, 2681->2677, 85];key_num(2), partition_key_num(1), is_distinct(FALSE), is_adaptive(0), MEM_USED(30720KB), DISK_USED(0KB)
4 BLKUP2:[8, 2681->2677, 85];ITPUX_SALES_LOG_CARD(ITPUX_SALES)
5 SSEK2:[8, 2681->2677, 85];scan_type(DESC), ITPUX_SALES_LOG_CARD(ITPUX_SALES), is_global(0), scan_range[(exp11-exp12,min),(max,max))
问题再现:SORT3 操作符再次出现!虽然使用了复合索引,但排序方向不匹配。
创建与 ORDER BY 子句完全一致的索引:
create index itpux_sales_logD_cardA on ITPUX_SALES(logtime desc, cardid asc);
执行计划:
1 NSET2:[9, 2681->2677, 85]
2 PRJT2:[9, 2681->2677, 85];exp_num(4), is_atom(FALSE)
3 BLKUP2:[8, 2681->2677, 85];ITPUX_SALES_LOGD_CARDA(ITPUX_SALES)
4 SSEK2:[8, 2681->2677, 85];scan_type(ASC), ITPUX_SALES_LOGD_CARDA(ITPUX_SALES), is_global(0), scan_range[(null2,min),(exp11-exp12,max))
最终效果:排序再次被消除!这次数据库通过正向扫描特殊设计的索引获得了正确结果。
ORDER BY 的顺序与索引扫描结果的顺序完全一致时,可消除排序| ORDER BY 子句 | 可消除排序的索引 | 扫描方向 |
|---|---|---|
A DESC, B DESC |
(A ASC, B ASC) |
反向扫描 |
A ASC, B ASC |
(A ASC, B ASC) |
正向扫描 |
A DESC, B ASC |
(A DESC, B ASC) |
正向扫描 |
SORT3:显式排序操作,消耗内存和CPUSSEK2:索引范围扫描,scan_type 显示扫描方向BLKUP2:通过索引回表取数据SORT 操作符的出现,这是优化的信号WHERE 条件和 ORDER BY 的列都考虑进索引文章
阅读量
获赞
