SQL 存储过程性能优化技巧

1次阅读

select * 在存储过程中伤性能因执行计划难复用、增加传输与内存压力、易引发隐式错误,且优化器跳过列裁剪,连带加载无用大字段;应显式指定字段。

SQL 存储过程性能优化技巧

为什么 SELECT * 在存储过程中特别伤性能

因为存储过程常被高频调用,而 SELECT * 会让执行计划无法复用、增加网络传输和内存压力,还容易因表结构变更引发隐式错误。更关键的是,sql Server 和 mysql 的查询优化器在面对 * 时,往往跳过列裁剪优化,连带加载大量无用字段(比如 TEXTjson 或大 BLOB)。

实操建议:

  • 显式列出所需字段,哪怕多打几下键盘——尤其避免在游标或临时表插入时写 SELECT *
  • 如果用到了 INSERT INTO #tmp SELECT *,先查清源表字段顺序和类型,改成 INSERT INTO #tmp (col1, col2) SELECT col1, col2 FROM ...
  • 在 SQL Server 中,用 SET STATISTICS IO ON 对比逻辑读差异;MySQL 可看 EXPLAIN format=JSON 里的 used_columns 字段

WITH RECOMPILE 不是万能缓存开关

很多人以为加了 WITH RECOMPILE 就能“解决参数嗅探”,结果反而让每次执行都硬编译,CPU 直线上升。它真正适用的场景极少:比如报表类存储过程,输入参数范围极广(@date_from 跨十年 vs 跨一天),且数据分布严重倾斜。

实操建议:

  • 优先尝试 OPTION (RECOMPILE) 加在具体语句后,而非整个过程加 WITH RECOMPILE —— 精准控制,减少编译开销
  • SQL Server 2016+ 可启用 QUERY_OPTIMIZER_HOTFIXES 或开启自动参数化,比硬加重编译更稳
  • MySQL 存储过程中没有等效机制,得靠应用层拆分逻辑,或用 PREPARE/EXECUTE 动态构造语句

临时表 vs 表变量:别只看语法简洁度

表变量(DECLARE @t table(...))看着干净,但 SQL Server 不会为它生成统计信息,优化器默认按 1 行估算;而临时表(CREATE TABLE #t)有统计信息、可建索引、支持事务回滚——但也会触发 tempdb 争用。

实操建议:

  • 记录数预估 > 100 行,一律用 #t;若只是存 2–3 个 ID 做过滤,@t 更轻量
  • 在 SQL Server 中,给 #t 加索引要趁早:CREATE INDEX ix ON #t(col) WHERE col IS NOT NULL(注意 WHERE 条件能帮优化器跳过空值扫描)
  • MySQL 没有表变量,只能用 CREATE TEMPORARY TABLE,但要注意它不支持外键、全文索引,且会话断开即删

WHERE 条件里写函数,等于主动放弃索引

WHERE YEAR(order_date) = 2024WHERE UPPER(name) = 'ABC' 这类写法,在绝大多数引擎里都会让索引失效。不是“可能不走”,而是优化器明确知道无法做索引 Seek,直接降级为 Scan。

实操建议:

  • 把函数移到右侧:用 WHERE order_date >= '2024-01-01' AND order_date 替代 <code>YEAR()
  • 需要大小写无关匹配?SQL Server 可建计算列 + 索引:ALTER TABLE t ADD name_upper AS UPPER(name),再在该列建索引
  • MySQL 8.0+ 支持函数索引:CREATE INDEX idx_name_up ON t ((UPPER(name))),但注意括号必须是双层

最常被忽略的一点:存储过程性能问题,八成出在第一次执行之后的执行计划缓存上。不是代码写得慢,是缓存了一个不适合当前参数的计划,又没被及时踢掉。盯住 sys.dm_exec_query_stats 里的 execution_countlast_elapsed_time,比反复改代码更管用。

text=ZqhQzanResources