意外的 #temp 表性能

4
开放赏金: 好的,大家,老板需要一个答案,我需要加薪。这似乎不是一个冷缓存问题。
更新: 我已经按照下面的建议去做了,但都没有效果。然而,客户端统计数据提供了一组有趣的数字。
#temp vs @temp 插入、删除和更新语句的数量 0 vs 1
插入、删除或更新语句影响的行数 0 vs 7647
选择语句的数量 0 vs 0
选择语句返回的行数 0 vs 0
事务的数量 0 vs 1
最有趣的是受影响的行数和事务的数量。提醒一下,下面的查询返回相同的结果集,只是以不同的表格样式呈现。
以下查询基本上在做相同的事情。它们都选择一组结果(约7000个),并将其填充到临时表或变量表中。在我的看法中,变量表@temp应该比临时表#temp更快地创建和填充,但第一个示例中的变量表需要1分15秒才能执行,而第二个示例中的临时表只需要16秒。
有人能给出解释吗?
declare @temp table ( 
id uniqueidentifier, 
brand nvarchar(255), 
field nvarchar(255),
date datetime, 
lang nvarchar(5), 
dtype varchar(50)
)
insert into @temp (id, brand, field, date, lang, dtype )
select id, brand, field, date, lang, dtype
from view 
where brand = 'myBrand' 
-- takes 1:15

vs

select id, brand, field, date, lang, dtype
into #temp
from view 
where brand = 'myBrand'

DROP TABLE #temp
-- takes 16 seconds

你看过两个查询的执行计划并发现它们有何不同吗? - John Naegle
1
在第一个例子中,您是指加载@cf还是@temp? - gbn
抱歉,好的,我会更新问题的... - gingerbreadboy
1
我还想分离出来看看区别是否在于“INSERT INTO”与“SELECT INTO”,而不是#temp与@temp。如果将#temp查询切换为使用INSERT INTO(提前创建#temp表),会发生什么? - Phil Sandler
请发布两次运行的 SET STATISTICS TIME ON 和 SET STATISTICS IO ON 的实际输出。 - Remus Rusanu
6个回答

9

我认为这几乎完全取决于表变量与临时表的性能。

表变量被优化为仅具有一行。当查询优化器选择执行计划时,它是基于(通常是错误的)假设,即表变量只有一行。

我找不到一个好的来源,但至少在这里提到了:

http://technet.microsoft.com/en-us/magazine/2007.11.sqlquery.aspx

其他相关来源:

http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=125052

http://databases.aspfaq.com/database/should-i-use-a-temp-table-or-a-table-variable.html


我非常喜欢这个答案,第一个链接中有很多值得深思的内容。它在很大程度上解释了表变量和临时表之间高差异的原因。当我回到服务器时,我将进一步调查。 - gingerbreadboy

4

使用 SET STATISTICS IO ON 和 SET STATISTICS TIME ON 两种方式运行。每种方式各运行6-7次,排除最好和最差的结果,然后比较两个平均时间。

我怀疑差异主要来自于冷缓存(第一次执行)与暖缓存(第二次执行)。STATISTICS IO 的输出将揭示这样的情况,因为在运行之间的物理读取量之间存在很大的差异。

并确保测试具有“实验室”条件:没有其他任务运行(没有锁争用),数据库(包括 tempdb)和日志已预先增长到所需大小,以便您不会遇到任何日志增长或数据库增长事件。


2

这种情况并不罕见。表变量在很多情况下(甚至经常)比临时表慢。以下是一些原因:

  • SQL Server为使用临时表的查询维护统计信息,但不为使用表变量的查询维护统计信息。没有统计数据,SQL Server可能会选择一个处理查询的差劲计划。

  • 除了为PRIMARY或UNIQUE约束创建的系统索引之外,表变量上不能创建非聚集索引。与具有非聚集索引的临时表相比,这会影响查询性能。

  • 表变量使用内部元数据,从而阻止引擎在并行查询中使用表变量(这意味着它无法利用多处理器计算机)。

  • SQL Server通过优化为一行设计了表变量(假定将返回1行数据)。


1

我不能百分之百确定这是原因,但是表变量不会有任何统计数据,而临时表会有。


"统计学" - 你能稍微详细解释一下吗? 谢谢
  • rrrg
- gingerbreadboy

1

SELECT INTO 是一种非记录操作,这可能解释了大部分性能差异。INSERT 为每个操作创建一个日志条目。

此外,SELECT INTO 在操作中创建表格,因此 SQL Server 自动知道它上面没有约束条件,这可能是一个因素。


1
@Table变量也不会被记录。 - Remus Rusanu

1
如果将7000条记录插入临时表(持久性或变量)需要超过一分钟的时间,那么性能问题几乎肯定出现在填充它的SELECT语句中。
在进行分析之前,您是否运行了DBCC FREEPROCCACHEDBCC DROPCLEANBUFFERS?我想可能是第二个查询正在使用一些缓存的结果。

DBCC FREEPROCCACHE难道不是专门用于存储过程的吗?我们并没有将其作为存储过程的一部分运行。 - gingerbreadboy
我曾经也这样认为,但实际上"proc cache"其实是"plan cache"。该语句会清除所有缓存的查询计划。在每次执行之前都应该运行它 - 如果你要比较两个不同的查询,你应该在每个查询之前使用这对语句单独进行分析。 - Aaronaught

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接