优先用@table_variable:数据量小、作用域窄、不需索引或事务回滚;必须用#temp_table:数据量大、需索引、跨嵌套存储过程访问或要求事务一致性。

直接说结论:数据量小、作用域窄、不需索引或事务回滚时,优先用 @table_variable;数据量大、需建索引、跨嵌套存储过程访问、或要求事务一致性时,必须用 #temp_table。
存储过程嵌套中能否共享临时数据
局部临时表 #temp 可被同一会话内的嵌套存储过程访问;表变量 @t 不行——它只在定义它的批处理(batch)内有效,子存储过程一进入就“不认识”这个变量,直接报错 Must declare the table variable "@t"。
- 外层存储过程创建
#temp→ 内层调用时可UPDATE #temp或SELECT * FROM #temp - 外层声明
@t TABLE(...)→ 内层执行INSERT INTO @t会失败 - 全局临时表
##global_temp虽可跨会话,但命名冲突风险高,且清理时机难控,非必要不选
数据量超过多少该换临时表
没有绝对阈值,但实测和 DBA 经验表明:当行数 > 1000 且含多列 JOIN / WHERE 过滤时,@table_variable 的性能会明显劣化。根本原因不是内存 vs 磁盘,而是查询优化器对表变量缺乏统计信息,常误判为“只有 1 行”,导致生成嵌套循环而非哈希连接,逻辑读飙升。
- 100 行以内:
@t通常更快,编译开销低,锁粒度小 - 1000–5000 行:视操作复杂度而定;若后续有
JOIN或GROUP BY,建议改用#t并加索引 - 5000+ 行:基本应切到
#t;否则可能触发 tempdb 内存溢出,反而更慢 - 注意:
DECLARE @t TABLE(... INDEX ix ON (col))在 SQL Server 2014+ 支持,但仅限创建时声明,不能事后CREATE INDEX
事务回滚是否影响临时数据
这是关键分水岭:#temp_table 参与事务,ROLLBACK 后数据消失;@table_variable 不参与事务,ROLLBACK 对它完全无效——插入的数据仍在。
- 需要原子性保证(如:先写日志临时表,再更新主表,失败则全退)→ 必须用
#t - 仅做中间计算,结果只用于当前步骤输出(如:汇总后直接
SELECT返回)→@t更轻量 - 别指望
TRUNCATE @t:语法错误;清空只能DELETE FROM @t,而TRUNCATE TABLE #t是高效选择
索引和执行计划稳定性
临时表能显式建索引,且优化器基于实际统计信息生成执行计划;表变量即使有索引(SQL Server 2014+),优化器仍倾向低估行数,尤其在多语句流程中 Plan Cache 复用率低。
-
CREATE INDEX IX_#t_col ON #t(col)可显著加速后续WHERE col = ?查询 -
DECLARE @t TABLE(id INT PRIMARY KEY, name VARCHAR(50))会隐式建聚集索引,但仅对id有效,无法覆盖复合查询条件 - 若存储过程中多次重用同一临时结构,且数据量波动大,用
#t+OPTION (RECOMPILE)比硬套@t更可靠
真正容易被忽略的点是:表变量在跨批处理(比如用 GO 分隔)时立即失效,而开发者常误以为“只要没 DROP 就还在”;另外,INSERT INTO @t EXEC sp_name 在所有版本中都不支持,必须改用 #t 中转。











