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逻辑。

举例

sql 复制代码
三张表:
    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 表连接,得到客户名称并排序

sql 复制代码
-- 第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;
相关推荐
笨小孩@GF 知行合一5 小时前
易语言-高级应用
数据库·编程·易语言·中文编程
KaiwuDB6 小时前
KaiwuDB 运维实战04:DRBD + KaiwuDB——物联网场景下的低成本数据库高可用方案
运维·数据库·物联网·时序数据库·kaiwudb·aiot·多模数据库
夜雪一千7 小时前
MySQL外键彻底入门:是什么、怎么用、什么时候不该用
数据库·mysql
qq_334466867 小时前
删除SSMS的历史登录记录
数据库·sql
zl_dfq8 小时前
Redis 之 【常见面试题总结】(从数据类型到高可用、缓存一致性全解析)
数据库·redis
龙亘川8 小时前
铭记英烈守初心,数字强基促服务:亘川智城退役军人服务系统的实践与价值
java·数据库·人工智能
kv1109 小时前
Android studio国内开发有关
java·数据库·android studio
哭泣方源炼蛊9 小时前
正则表达式c++内容汇集
数据库·c++·mysql·正则表达式
余槐i9 小时前
EXPLAIN 显示走了索引查询仍慢:回表与选择性在 500 万行表上拉开 10 倍耗时
数据库·postgresql·性能优化·vacuum·explain