什么时候应该在sql server中使用表变量vs临时表?

yma*_*man 283 sql-server temp-tables table-variable

我正在学习表变量的更多细节.它表示临时表总是在磁盘上,表变量在内存中,也就是说,表变量的性能优于临时表,因为表变量比临时表使用更少的IO操作.

但有时,如果表变量中的记录太多而无法包含在内存中,则表变量将像临时表一样放在磁盘上.

但我不知道"太多记录"是什么.100,000条记录?或1000,000条记录?我怎么知道我正在使用的表变量是在内存中还是在磁盘上?SQL Server 2005中是否有任何函数或工具来测量表变量的大小或让我知道何时将表变量从内存放入磁盘?

Mar*_*ith 349

您的问题表明您已经屈服于围绕表变量和临时表的一些常见误解.

在DBA网站上写了相当广泛的答案,看看两种对象类型之间的差异.这也解决了你关于光盘与内存的问题(我没有看到两者之间的行为有任何显着差异).

关于标题中的问题,关于何时使用表变量与本地临时表,您并不总是有选择.例如,在函数中,只能使用表变量,如果需要在子作用域中写入表,那么只有#temp表可以执行(表值参数允许只读访问).

您可以在哪里选择一些建议如下(尽管最可靠的方法是简单地测试您的特定工作量).

  1. 如果您需要一个无法通过#temporaryUNIQUE约束隐式创建的索引,那么您需要一个PRIMARY KEY表,因为无法在表变量上创建这些索引.(此类索引的示例是非唯一索引,筛选索引或具有CREATE INDEXd列的索引).注意:SQL Server 2014将允许为表变量内联声明非唯一索引.
  2. 如果要从表中重复添加和删除大量行,则使用INCLUDE表.这支持#temporary(比TRUNCATE大表更有效),并且随后的后续插入DELETE可以具有比TRUNCATE 这里所示的更好的性能.
  3. 如果要删除或更新大量行,那么临时表可能比表变量好得多 - 如果它能够使用行集共享(参见下面的"行集共享的效果").
  4. 如果使用该表的最佳计划将根据数据而变化,则使用DELETE表格.这支持创建统计信息,允许根据数据动态重新编译计划(尽管对于存储过程中缓存临时表,需要单独理解重新编译行为).
  5. 如果使用该表的查询的最佳计划不太可能发生变化,那么您可以考虑使用表变量来跳过统计信息创建和重新编译的开销(可能需要提示来修复所需的计划).
  6. 如果插入表中的数据源来自可能很昂贵的#temporary语句,那么考虑使用表变量将阻止使用并行计划的可能性.
  7. 如果您需要表中的数据以回滚外部用户事务,那么请使用表变量.可能的用例可能是在长SQL批处理中记录不同步骤的进度.
  8. SELECT在用户事务中使用表时,锁可以保持比表变量更长的时间(可能直到事务结束与声明结束取决于锁和隔离级别的类型),并且它还可以防止截断#temp事务日志,直到用户事务结束.所以这可能有利于使用表变量.
  9. 在存储的例程中,可以缓存表变量和临时表.缓存表变量的元数据维护小于表的元数据维护tempdb.Bob Ward在他的#temporary演讲中指出,这可能会导致在高并发条件下对系统表进行额外争用.此外,在处理少量数据时,这会对性能产生可测量的差异.

行集共享的效果

DECLARE @T TABLE(id INT PRIMARY KEY, Flag BIT);

CREATE TABLE #T (id INT PRIMARY KEY, Flag BIT);

INSERT INTO @T 
output inserted.* into #T
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY @@SPID), 0
FROM master..spt_values v1, master..spt_values v2

SET STATISTICS TIME ON

/*CPU time = 7016 ms,  elapsed time = 7860 ms.*/
UPDATE @T SET Flag=1;

/*CPU time = 6234 ms,  elapsed time = 7236 ms.*/
DELETE FROM @T

/* CPU time = 828 ms,  elapsed time = 1120 ms.*/
UPDATE #T SET Flag=1;

/*CPU time = 672 ms,  elapsed time = 980 ms.*/
DELETE FROM #T

DROP TABLE #T
Run Code Online (Sandbox Code Playgroud)

  • 嗨,马丁史密斯先生.在mi的情况下,我只想存储一组ID值,以便在Store过程中的其他查询中使用它们.那么你推荐我什么? (2认同)

Aba*_*cus 67

如果是非常少量的数据(数千字节),请使用表变量

使用临时表来获取大量数据

另一种思考方式:如果您认为可能会从索引,自动统计或任何SQL优化器中获益,那么您的数据集可能对于表变量来说太大了.

在我的示例中,我只想将大约20行放入格式中并将它们作为一组修改,然后再使用它们来更新/插入永久表.所以表变量是完美的.

但我也运行SQL同时回填数千行的,我可以肯定地说,该临时表进行比表变量更好.

这与CTE如何关注类似尺寸的原因没有什么不同 - 如果CTE中的数据非常小,我发现CTE的性能与优化器的性能一样好或更好,但是如果它相当大,那么它伤害了你.

我的理解主要基于http://www.developerfusion.com/article/84397/table-variables-v-temporary-tables-in-sql-server/,它有更多细节.


Pau*_*urm 38

微软在这里说

表变量没有分布统计信息,它们不会触发重新编译.因此,在许多情况下,优化器将在假设表变量没有行的情况下构建查询计划.因此,如果您期望有更多行(大于100),则应谨慎使用表变量.在这种情况下,临时表可能是更好的解决方案.


小智 14

我完全同意Abacus(对不起 - 没有足够的评论点).

此外,请记住,它不一定取决于您拥有多少条记录,而是记录的大小.

例如,您是否考虑过1,000条记录(每条记录50列)与100,000条记录(每条记录只有5列)之间的性能差异?

最后,也许您查询/存储的数据超出了您的需求?这是一篇关于SQL优化策略的好读物.限制你拉动的数据量,特别是如果你没有全部使用它们(一些SQL程序员确实很懒,只是选择一切,即使他们只使用一小部分).不要忘记SQL查询分析器也可能成为你最好的朋友.