表变量不参与事务回滚、嵌套过程不可见、无统计信息导致性能差;临时表支持回滚、跨层访问、可建索引;选择依据应为数据量、查询复杂度及执行计划稳定性,而非绝对行数阈值。

表变量在存储过程里不参与事务回滚
这是最容易踩坑的一点:用 @temp_table 存数据,再执行 ROLLBACK,你会发现里面的数据还在。因为表变量本身不被事务日志记录,事务回滚只影响普通表和临时表。如果你的业务逻辑依赖“临时数据随事务一起撤销”,比如订单拆分失败要整体回退,那就必须用 #temp_table,不能用表变量。
常见错误现象:INSERT INTO @temp SELECT ... 后执行 ROLLBACK,后续仍能查到数据,导致误判或重复处理。
- 表变量适合:中间状态暂存、不影响主事务逻辑的转换步骤
- 临时表适合:需要与主事务强一致的中间结果(如预校验、分步写入)
- 注意:
CREATE TABLE #t和SELECT INTO #t创建的临时表都支持事务回滚
嵌套存储过程中访问外层临时数据
内层存储过程能不能读写外层定义的临时结构?答案是:可以访问 #temp_table,但完全看不到 @temp_table。
原因在于作用域机制——表变量只存在于定义它的批处理或存储过程内部;而本地临时表(#开头)在当前会话内可见,且能被同一会话中调用的嵌套存储过程直接引用。
- 典型场景:外层过程生成筛选ID列表,内层过程基于该列表做批量更新
- 如果用表变量,内层会报错
Must declare the table variable "@xxx" - 全局临时表(
##)虽可跨会话,但命名冲突风险高,非必要不推荐
大数据量下索引和统计信息缺失导致性能骤降
当临时数据超过几百行,尤其是要做 JOIN、GROUP BY 或 WHERE 过滤时,@temp_table 几乎必然比 #temp_table 慢得多——它没有统计信息,优化器只能按固定基数估算,常选错执行计划。
临时表则支持显式建索引:CREATE INDEX IX_#t_id ON #t(id),还能触发统计信息自动更新(取决于 SET STATISTICS 设置)。
- 1000 行以内、单次查询、无复杂条件:表变量够用
- 5000+ 行、多步关联、需 WHERE + ORDER BY:优先建临时表并加索引
- 避免误区:以为“表变量在内存所以一定快”——数据溢出到 tempdb 后反而更慢
存储过程重编译开销差异
使用表变量的存储过程,SQL Server 可以缓存并复用执行计划;而频繁 CREATE TABLE #t / DROP TABLE #t 会导致每次调用都触发重编译,尤其在高并发场景下明显拖慢响应。
但这不是绝对优势——如果表变量结构固定、数据量稳定,确实省去编译成本;但如果因数据量突增导致执行计划失效(例如原本走索引查找,后来变成全表扫描),缓存的旧计划反而有害。
- 高频调用、结构简单、数据量波动小 → 表变量更稳
- 低频调用、数据量变化大、需精准执行计划 → 临时表 + 显式索引更可控
- 临时表创建后不删(靠会话结束自动清理)比反复建删更轻量
SET STATISTICS IO ON 看逻辑读,用执行计划对比实际行数与估计行数偏差,才是可靠依据。











