临时表在多数真实业务场景下性能更好,但前提是数据量够大、有JOIN或需要多次读写;表变量只在极轻量、单次使用的场景里“看起来快”——它快是因为绕过了很多机制,不是真高效。

临时表在多数真实业务场景下性能更好,但前提是数据量够大、有JOIN或需要多次读写;表变量只在极轻量、单次使用的场景里“看起来快”——它快是因为绕过了很多机制,不是真高效。
什么时候用 #temp 而不是 @table
关键看三点:数据规模、是否参与连接、是否重复访问。
- 数据行数 > 100 行,尤其超过 1000 行时,
#temp更稳。优化器能基于统计信息选哈希连接(Hash Join),而@table强制走嵌套循环(Nested Loop),JOIN 性能断崖式下跌 - 需要对中间结果建索引(比如
CREATE INDEX IX_ProductID ON #ProductUpdates(ProductID)),只有#temp支持;@table不支持索引(SQL Server 2014+ 允许带主键/唯一约束,但不等价于真正索引) - 同一存储过程中多次
SELECT或UPDATE同一中间集,#temp可复用物理数据;@table每次都得重算、重读,反而放大 I/O - 涉及
INSERT ... SELECT+ 后续JOIN的典型 ETL 场景(如库存批次更新),实测#temp比@table快 3–5 倍
为什么 @table 在高并发下“有时更快”
这不是它本身快,而是它避开了 tempdb 的争抢路径:
-
@table不写tempdb的系统表(如sysobjects),不触发存储过程重编译,高并发下避免了WRITELOG等待 - 它没统计信息,优化器永远按“1 行”估算,计划稳定——但这只是把问题从“计划抖动”换成“计划错得固定”,仅当实际数据也确实是 ≤100 行且不 JOIN 时才不翻车
- 作用域严格限制在当前批处理内,不会被动态 SQL(
EXEC sp_executesql)意外污染,适合纯内存级暂存
SELECT INTO #t 和 DECLARE @t TABLE(...) 的底层差异
二者根本不是同类东西:
-
SELECT INTO #t是 DDL 操作,会分配页、记日志、更新tempdb元数据,后续可CREATE INDEX、可UPDATE、可统计信息自动更新 -
DECLARE @t TABLE(...)是变量声明,初始分配在内存,溢出才落盘,且落盘路径不走完整事务日志,不可加非聚集索引,INSERT后无法UPDATE STATISTICS - 若你在
@t上跑INNER JOIN却没加OPTION (HASH JOIN)提示,执行计划大概率已劣化,但你根本看不到警告——因为优化器压根不分析它
真正容易被忽略的是:选错不是“慢一点”,而是让执行计划彻底失效。一个 @table 在开发环境跑得飞快(因为数据只有 5 行),上线后批量处理 5 万条,CPU 直接拉满、查询卡死,问题根源不在数据量,而在优化器从第一行就选错了连接算法。


















