前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >108-一个SQL, 两种优化方法, 效果都还可以

108-一个SQL, 两种优化方法, 效果都还可以

作者头像
老虎刘
发布2023-09-01 13:25:26
1770
发布2023-09-01 13:25:26
举报

你们关心功能, 我来提升性能, 这里是<老虎刘谈SQL优化>, 今天介绍的这个案例来自一个金融行业的生产系统.

我在对系统做主动性能检查时,发现了一个执行时间长达30多秒的SQL, 3个相关表的记录数都只有几十万条, 可能存在较大的优化空间. 经过分析,并与业务人员进行沟通, 我对SQL进行了改写(两种方法), 改写后的SQL执行效率都有几十倍的提升.

下面是生成Test Case的脚本(测试环境:数据库版本19.17, Linux虚拟机@笔记本):

代码语言:javascript
复制
--创建两个表(结构都一样):
create table T_DEMO_648_QDII_H(i_code varchar2(20)
,beg_date date,end_date date,DP_CLOSE number);

create table T_DEMO_648_Wind_W(i_code varchar2(20)
,beg_date date,end_date date,DP_CLOSE number);

--为 T_DEMO_648_QDII_H表(以下简称H表)生成 74.4万记录, 
--大概占用存储空间24M
declare
  v_beg_date date :=date'2010-01-01';
  v_end_date date :=date'2023-08-01';
  c_date date;
begin
   for i in 1..150 loop
     c_date:=v_beg_date;
     while v_end_date>c_date  loop
       insert into T_DEMO_648_QDII_H values('C'||lpad(i,3,'0'),
          c_date,c_date+1,round(dbms_random.value(1,10)));
       c_date:=c_date+1;
     end loop;
   end loop;
   commit;
end;
/

---为T_DEMO_648_Wind_W表(以下简称W表) 生成34w+记录, 
--大概占用存储空间12M
declare
  n number;
  v_beg_date date :=date'2010-01-01';
  v_end_date date :=date'2023-08-01';
  c_date date;
begin
   for i in 1..150 loop
     c_date:=v_beg_date;
     while v_end_date>c_date  loop
       n:=round(dbms_random.value(0,1)*0.6)*7+1;
       insert into T_DEMO_648_Wind_W values('C'||lpad(i,3,'0'),
          c_date,c_date+n,round(dbms_random.value(1,10)));
       c_date:=c_date+n;
     end loop;
   end loop;
   commit;
end;
/
 
--收集两表统计信息:
exec dbms_stats.gather_table_stats(user,'T_DEMO_648_QDII_H');
exec dbms_stats.gather_table_stats(user,'T_DEMO_648_Wind_W');

通过观察W表的数据分布并得到业务人员确认:对于同一个I_code值的每一条记录, 大部分记录的end_date比beg_date大一天, 少量记录的间隔会大于一天; 日期范围不存在交叉. 部分记录如下图所示:

业务SQL(做了简化处理, 用count是为了方便测试):

代码语言:javascript
复制
select count(w_dp_close) from
(
select h.*,w.dp_close as w_dp_close
from  T_DEMO_648_QDII_H H              
LEFT JOIN T_DEMO_648_Wind_W W  
    ON   H.I_CODE = W.I_CODE         
     AND W.BEG_DATE <= H.BEG_DATE  
     AND W.END_DATE >  H.BEG_DATE
);

SQL执行时间47秒+, 执行计划如下:

根据业务SQL的实现逻辑与相关表的数据分布规律, 老虎刘给出如下两种优化改写方法:

优化方法1:

使用connect by将W表日期间隔大于1天的记录, 拆分成多条一天间隔的记录,然后做等值关联, 改写后的SQL执行时间为1.84秒.

代码语言:javascript
复制
select count(w_dp_close)
from
(
select h.*,x.dp_close as w_dp_close 
from T_DEMO_648_QDII_H H
left join 
    (
    select i_code,w.beg_date+level-1 as beg_date,end_date ,w.dp_close
    from  T_DEMO_648_Wind_W W 
    connect by level<=end_date-beg_date
    and prior rowid=rowid
    and prior dbms_random.value is not null
    )x
on h.i_code=x.i_code and h.beg_date=x.beg_date
);

SQL对应执行计划如下:

优化方法2:

使用区间检索+标量子查询, 这个写法需要W表上创建对应的索引来配合, 执行时间1.39秒.

代码语言:javascript
复制
create index idx_T_DEMO_648_Wind_W_1 on T_DEMO_648_Wind_W(i_code,end_date);

select count(w_dp_close)
from
(
select H.*
   ,(select dp_close from 
         (select w.dp_close,w.beg_date 
          from  T_DEMO_648_Wind_W W 
          where  H.beg_date<W.end_date and H.i_code=W.i_code 
          order by W.end_date
         )x 
     where rownum<=1 and H.beg_date>=x.beg_date
    ) as w_dp_close
from T_DEMO_648_QDII_H H
);

SQL对应执行计划如下:

很多数据库中都隐藏着大量的低效SQL等待优化处理, 是时候找个专业人士做个检查了.

(全文结束)

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-08-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老虎刘谈oracle性能优化 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档