SQL查询执行耗时30分钟 如何优化慢SQL提升性能
SQL慢查询优化方案
你的查询执行耗时长达30分钟,核心问题是无效全量计算、过滤条件下推失败、关联逻辑冗余、缺失必要索引,以下是可直接落地的优化步骤:
1. 优先下推过滤条件,消除无效计算
你当前的写法是先对全表所有历史数据做聚合、关联计算,最后才在外层筛选2022年1-3月、类型为1、指定卖方税号的小范围数据,90%以上的计算都是完全无效的。
直接把所有外层过滤条件挪到最内层关联逻辑中,同时删掉永远不会命中的分支:
- 直接加
t.state = 1(对应外层env=1的筛选) - 直接加
ct.type = 1(对应外层a.type=1的筛选,直接删掉原逻辑中ct.type=2的所有分支,因为最终结果完全不需要这部分数据) - 直接加日期范围筛选
t.declaration_date between to_date('01-01-2022','dd-mm-yyyy') and to_date('31-03-2022','dd-mm-yyyy') - 直接加卖方税号筛选
t.g2_code2 = '302857893'(ct.type=1时seller_tin就是t.g2_code2,不需要等case when计算完再过滤) - 只保留
t.g1_b in (10,11)的条件,删掉ct.type=2对应的t.g1_b=40逻辑 - 因为已经固定
ct.type=1、t.state=1,所有case when判断都可以直接替换为固定值/对应字段,省去逐行判断的开销。
2. 消除冗余子查询,减少表扫描次数
你写的g1、g2两个子查询都重复扫描了valyuta_goods全表做分组,再和外层的g表关联,属于完全冗余的逻辑:
- 去掉两个子查询中对
valyuta_goods的重复扫描,直接关联支付表、逾期支付表按商品ID聚合即可 - 替换老式的
(+)外连接写法为标准LEFT JOIN语法,方便优化器生成正确的执行计划 - 删掉子查询中不参与关联、不参与计算的冗余字段(比如原g1/g2子查询里的
declaration_id、cost_facture完全没有被使用),减少分组计算的开销。
3. 添加匹配的联合索引,避免全表扫描
没有对应索引的话,即使SQL逻辑调整正确,依然会走全表扫描拖慢速度,按优先级创建以下覆盖索引即可避免回表查询:
valyuta_declarations表:创建联合索引idx_decl_query(type, state, declaration_date, g1_b, g2_code2, id, num, curr_Course, G2_NAME)valyuta_CONTRACT_SUB_TYPES表:创建联合索引idx_contract_short(short_name, type)valyuta_GOODS表:创建联合索引idx_goods_decl(DECLARATION_ID, id, name, tnved_code, g31_amount, brutto, netto, cost_facture)valyuta_good_payments表:创建联合索引idx_gp_goodid(good_id, payment_code, payment_amount)valyuta_GOOD_PAY_LATE表:创建联合索引idx_gpl_goodid(good_id, L_G47_TYPE, L_G47_SUM)
优化后参考SQL(逻辑与原SQL完全一致)
select t.id, t.num, t.declaration_Date, ct.type, g.name, g.tnved_code, g.g31_amount, g.brutto, g.netto, 1 as env, t.g2_code2 as seller_tin, '200794867' as buyer_tin, t.g2_name as seller_name, t.g2_name as buyer_name, sum(g.cost_facture * t.curr_Course) as cost_Facture, SUM(gp.good_payment_27 + gpl.good_pay_late_27) as good_payment_27, SUM(gp.good_payment_29 + gpl.good_pay_late_29) as good_payment_29 from valyuta_declarations t join valyuta_CONTRACT_SUB_TYPES ct on t.type = ct.short_name join valyuta_GOODS g on t.id = g.DECLARATION_ID left join ( select good_id, SUM(CASE WHEN payment_code = 27 THEN payment_amount ELSE 0 END) as good_payment_27, SUM(CASE WHEN payment_code = 29 THEN payment_amount ELSE 0 END) as good_payment_29 from valyuta_good_payments group by good_id ) gp on g.id = gp.good_id left join ( select good_id, SUM(CASE WHEN L_G47_TYPE = 27 THEN L_G47_SUM ELSE 0 END) as good_pay_late_27, SUM(CASE WHEN L_G47_TYPE = 29 THEN L_G47_SUM ELSE 0 END) as good_pay_late_29 from valyuta_GOOD_PAY_LATE group by good_id ) gpl on g.id = gpl.good_id where ct.type = 1 and t.state = 1 and t.g1_b in (10, 11) and t.declaration_date between to_date('01-01-2022','dd-mm-yyyy') and to_date('31-03-2022','dd-mm-yyyy') and t.g2_code2 = '302857893' GROUP BY t.id, t.num, t.declaration_Date, ct.type, g.name, g.tnved_code, g.g31_amount, g.brutto, g.netto, t.g2_code2, t.g2_name
按以上步骤调整后,查询耗时通常可以降到秒级。如果执行依然偏慢,可以查看执行计划确认是否存在未走索引的全表扫描节点,针对性调整索引即可。
内容的提问来源于stack exchange,提问作者Kudrat
相关产品推荐
相关产品推荐

