SQL优化案例:存储过程、复杂SQL拆成简单SQL提升整体性能

环境

系统平台:N/A
版本:9.0,4.5

文档用途

本文介绍通过将存储过程、复杂SQL拆成简单SQL提升执行性能。

详细信息

将复杂SQL拆分成多个简单SQL,是数据库性能优化中常用且有效的手段。这种做法通常能带来显著的性能提升,但需要理解其背后的原理,并根据具体情况谨慎实施。

复杂SQL

一个复杂的SQL语句(例如包含多表连接、子查询、聚合函数、窗口函数等)在执行时,可能面临以下问题:

1、优化器选择不佳:SQL越复杂,优化器的执行计划选择空间越大,可能选择一个并非最优的计划(例如错误的连接顺序、未使用索引)
2、资源消耗集中:单个复杂查询可能消耗大量内存(如哈希连接需要构建哈希表)、临时空间(排序、分组),导致系统资源紧张
3、锁持有时间长:如果查询涉及大量数据修改或长时间运行,会长时间持有锁,影响并发
4、中间结果集大:多表连接可能产生巨大的中间结果集,即使最终结果很小,也会消耗大量I/O和CPU
5、统计信息不准确:复杂查询依赖统计信息,如果统计信息过时,优化器可能误判

拆分优点

将复杂SQL拆分为多个简单SQL,本质上是将优化器的部分工作“手动”接管,通过分步执行来优化性能:

1、简化每个步骤:每个简单查询更容易利用索引,优化器也能更快找到最优计划
2、减少中间结果规模:通过提前过滤、聚合,将中间结果物化到临时表,后续查询只需处理较小的数据集
3、降低资源峰值:将大查询分解为多个小查询,避免一次性消耗大量内存或临时空间,使资源使用更平滑
4、提高并发性:短查询持有锁的时间更短,减少阻塞
5、便于调试和监控:可以单独分析每一步的性能,更容易定位瓶颈
6、利用临时表索引:将中间结果存入临时表后,可以为其创建索引,加速后续连接或过滤
7、实现渐进式处理:对于超大数据量,可以分批处理(如分页、按日期范围切割),避免一次性处理全部数据

常见拆分方法

1、使用中间表分解多步逻辑

中间表可以使用普通表、TEMP表、UNLOGGED表暂存数据,使用“最小结果集”进行下一步操作。

2、分批处理

对于无法避免全表扫描的超大表,可以按某个范围(如ID范围、日期)分批处理,最后合并结果。

3、使用汇总表

对于非实时但频繁执行的复杂报表查询,可以事先通过存储过程定时计算并存储到汇总表中,查询时直接读取汇总表。

4、MERGE INTO/UPSERT语法改为truncate和insert逻辑

MERGE INTO/UPSERT操作大量数据时可将SQL改写为truncate和insert … select … from逻辑。

举例

三张表:
    customers(客户):customer_id,name
    orders(订单):order_id,customer_id,order_date
    order_details(订单明细):detail_id,order_id,product_id,quantity,price

  需求:
    统计每个客户在过去一年内的订单总金额(按客户分组求和),并按金额降序排序。

SELECT c.customer_id, c.name, SUM(od.quantity * od.price) AS total_amount
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
JOIN order_details od ON o.order_id = od.order_id
WHERE o.order_date >= CURRENT_DATE - INTERVAL '1 year'
GROUP BY c.customer_id, c.name
ORDER BY total_amount DESC;

拆分思路

1、先从 orders 和 order_details 中提取近一年的订单明细(只保留需要的字段),存入临时表
2、在临时表上按客户聚合金额
3、最后与 customers 表连接,得到客户名称并排序

-- 第1步:提取近一年的订单明细,存入临时表
CREATE TEMP TABLE temp_order_amount AS
SELECT
    o.customer_id,
    od.quantity * od.price AS amount
FROM orders o
JOIN order_details od ON o.order_id = od.order_id
WHERE o.order_date >= CURRENT_DATE - INTERVAL '1 year';

-- 在临时表上创建索引,加速后续聚合和连接
CREATE INDEX idx_temp_customer ON temp_order_amount(customer_id);

-- 第2步:按客户聚合金额
CREATE TEMP TABLE temp_customer_total AS
SELECT
    customer_id,
    SUM(amount) AS total_amount
FROM temp_order_amount
GROUP BY customer_id;

-- 第3步:连接客户表,获取名称并排序
SELECT
    c.customer_id,
    c.name,
    t.total_amount
FROM customers c
JOIN temp_customer_total t ON c.customer_id = t.customer_id
ORDER BY t.total_amount DESC;

-- 清理临时表(可选,会话结束自动删除)
DROP TABLE temp_order_amount;
DROP TABLE temp_customer_total;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值