应优先用临时表替代表变量,及时建索引并更新统计信息;postgresql宜用物化视图固化高频计算结果;mysql需避免select into多次赋值;所有数据库均须禁用含实时时间函数的中间结果缓存。

SQL Server 中用临时表替代表变量缓存中间结果
表变量(@temp_table)在存储过程中看似轻量,但实际不参与统计信息更新、无索引支持(除主键/唯一约束外)、且执行计划常被误判为“小数据集”,导致后续 JOIN 或聚合性能骤降。尤其当中间结果超 100 行,或需多次引用时,它反而比临时表更慢。
实操建议:
- 把
DECLARE @result TABLE (id INT, val DECIMAL(18,2))改成CREATE TABLE #result (id INT, val DECIMAL(18,2)) - 对高频查询字段立刻建索引:
CREATE INDEX IX_result_id ON #result(id)(注意:不能在 CREATE 语句里直接加,必须分步) - 如果中间结果需跨多个分支逻辑复用,务必在首次填充后显式
UPDATE STATISTICS #result,避免优化器沿用过期基数估算
PostgreSQL 用 MATERIALIZED VIEW 固化高频计算结果
当某个复杂查询(如多层窗口函数 + 关联维度表)被多个存储过程反复调用,且数据变更不频繁(例如每日批处理后更新),硬编码重复计算就是浪费。PostgreSQL 的物化视图能真正落盘保存结果,比每次执行 WITH CTE 快一个数量级。
实操建议:
- 创建时加
NO DATA避免阻塞初始化:CREATE MATERIALIZED VIEW mv_daily_summary AS SELECT ... WITH DATA - 刷新必须显式触发:
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_daily_summary(注意:并发刷新要求有唯一索引) - 不要在存储过程中直接
SELECT * FROM mv_daily_summary后再过滤——先在物化视图定义里把 WHERE 条件收进去,减少运行时扫描量
MySQL 存储过程中避免 SELECT ... INTO 多次赋值引发隐式锁
SELECT col INTO @var FROM t WHERE id = ? 看似简单,但如果在循环内反复执行,每条都会触发一次独立查询 + 行锁(即使只读),并发高时容易卡住。更糟的是,@var 是会话级变量,若存储过程嵌套调用,值可能被意外覆盖。
实操建议:
- 批量提取改用临时表:
CREATE TEMPORARY TABLE tmp_cache AS SELECT id, calc_col FROM t WHERE ... - 需要单值时,优先用
SELECT MAX(calc_col) FROM t WHERE ...而非INTO,让优化器走索引覆盖 - 绝对避免在游标循环里用
INTO赋值后再做计算——把计算逻辑全下推到 SELECT 子句中,例如:SELECT id, price * tax_rate AS final_price INTO ...
所有数据库都该禁用的缓存陷阱:依赖 GETDATE() 或 NOW() 的中间结果
只要中间结果里用了 GETDATE()(SQL Server)、NOW()(MySQL)、CURRENT_TIMESTAMP(PostgreSQL),哪怕只出现一次,整个结果集就失去可重入性。缓存该结果等于缓存“过期快照”,下游再怎么加索引也救不回来。
实操建议:
- 把时间基准抽成参数传入存储过程,例如
@as_of_date DATETIME2,所有计算基于它,而非实时函数 - 若必须动态取当前时间,只在最终输出层调用,中间表/临时表一律用参数化时间字段
- 检查执行计划里是否出现
Compute Scalar(SQL Server)或Function Scan(PostgreSQL)节点——那是实时函数正在拖慢缓存有效性
缓存中间值不是加个 # 或 MATERIALIZED 就完事;关键在时间语义是否可控、统计信息是否新鲜、以及那个“中间值”到底算不算真正的中间——还是只是把错误推迟到下游才暴露。










