优先用表变量代替临时表,但数据量超100行或需统计信息时必须用临时表;表变量无统计信息、不走tempdb分配路径,临时表反之;函数内只能用表变量;触发器中临时表易引发tempdb闩锁争用。

优先用表变量代替临时表,但数据量超过100行或需统计信息时必须切回临时表。 这不是权衡取舍,而是 SQL Server 优化器对两种对象的处理机制根本不同——表变量没统计信息,临时表有;表变量不走 tempdb 分配路径,临时表必须走。
什么时候该用 @table 而不是 #temp
表变量适合轻量、确定性小、生命周期短的场景:
- 存配置项、参数列表、校验结果等 ≤ 100 行的数据,比如
DECLARE @Config TABLE (Key NVARCHAR(50), Value SQL_VARIANT) - 函数内唯一合法选择——
#temp在用户定义函数中直接编译失败,报错Invalid object name '#MyTemp' - 不需要索引、不参与复杂 JOIN、不依赖执行计划质量的中间结果
- 注意:
@t不支持ALTER TABLE,也不能在INSERT INTO @t EXEC ...中作为目标
为什么 #temp 在触发器里特别危险
触发器高频调用 + 短事务 + 隐式建删,会让 #temp 成为 TempDB 闩锁争用的放大器:
- 每次触发都新建
#t→ 抢同一组 GAM/SGAM/PFS 页 → 出现PAGELATCH_UP等待 -
SELECT INTO #t每次都重新分配空间,无法复用执行计划,还绕过统计信息更新 - 启用 RCSI 后,
#t上的 DML 会额外写入 version_store,加重version_store_kb消耗 - 若触发器嵌套或批处理中多次执行,
#t反复建删 → PFS 页面频繁更新
真要用 #temp,至少做这三件事
不能只改写法,得从结构、生命周期和底层配置协同控制:
- 显式声明结构:
CREATE TABLE #t (id INT, name NVARCHAR(50)),禁用SELECT INTO #t - 开头加清理逻辑:
IF OBJECT_ID('tempdb..#t') IS NOT NULL DROP TABLE #t,防异常退出后残留干扰监控 - 确保 tempdb 已按 CPU 核心数配置多文件(如 16 核服务器设 8 个等大文件),否则再怎么优化语句也扛不住分配争用
- 大数据量(>1000 行)必须用
#temp,且首次插入后立即执行UPDATE STATISTICS #t,否则优化器仍按“空表”估算,导致Table Scan替代Index Seek
最常被忽略的一点:临时表只是症状,不是病因。真正压垮 TempDB 的,往往是本该异步处理的日志归档、通知推送、跨库聚合等逻辑,硬塞进事务上下文里执行。拆出去,比怎么写 #t 都管用。











