最后是一个测试脚本,用于比较@table 变量和#temp 表之间的性能。我想我已经正确设置了——性能计时是在 DELETE/TRUNCATE 命令之外进行的。我得到的结果如下(以毫秒为单位)。
@Table Variable #Temp (delete) #Temp (truncate)
--------------- -------------- ----------------
5723 5180 5506
15636 14746 7800
14506 14300 5583
14030 15460 5386
16706 16186 5360
只是为了确保我是理智的,这表明 CURRENT_TIMESTAMP (aka ) 是在语句时获取的,而不是批处理时,因此 TRUNCATE/DELETE 与语句GetDate()
之间应该没有交互。SET @StartTime = CURRENT_TIMESTAMP
select current_timestamp
waitfor delay '00:00:04'
select current_timestamp
-----------------------
2012-10-21 11:29:20.290
-----------------------
2012-10-21 11:29:24.290
当使用DELETE清表时,第一次运行和后续运行之间的跳转是相当一致的。我对DELETE的理解缺少什么?我已经重复了很多次,交换了顺序,将 tempdb 调整为不需要增长等。
CREATE TABLE #values (
id int identity primary key, -- will be clustered
name varchar(100) null,
number int null,
type char(3) not null,
low int null,
high int null,
status smallint not null
);
GO
SET NOCOUNT ON;
DECLARE @values TABLE (
id int identity primary key clustered,
name varchar(100) null,
number int null,
type char(3) not null,
low int null,
high int null,
status smallint not null
);
DECLARE @ExecutionTime TABLE( Duration bigINT )
DECLARE @StartTime DATETIME, @i INT = 1;
WHILE (@i <= 5)
BEGIN
DELETE @values;
DBCC freeproccache With NO_InfoMSGS;
DBCC DROPCLEANBUFFERS With NO_InfoMSGS;
SET @StartTime = CURRENT_TIMESTAMP -- alternate getdate()
/****************** measured process ***********************/
INSERT @values SELECT a.* FROM master..spt_values a join master..spt_values b on b.type='P' and b.number < 1000;
/**************** end measured process *********************/
INSERT @ExecutionTime
SELECT DurationInMilliseconds = datediff(ms,@StartTime,CURRENT_TIMESTAMP)
SET @i += 1
END -- WHILE
SELECT DurationInMilliseconds = Duration FROM @ExecutionTime
GO
-- Temporary table
DECLARE @ExecutionTime TABLE( Duration bigINT )
DECLARE @StartTime DATETIME, @i INT = 1;
WHILE (@i <= 5)
BEGIN
delete #values;
-- TRUNCATE TABLE #values;
DBCC freeproccache With NO_InfoMSGS;
DBCC DROPCLEANBUFFERS With NO_InfoMSGS;
SET @StartTime = CURRENT_TIMESTAMP -- alternate getdate()
/****************** measured process ***********************/
INSERT #values SELECT a.* FROM master..spt_values a join master..spt_values b on b.type='P' and b.number < 1000;
/**************** end measured process *********************/
INSERT @ExecutionTime
SELECT DurationInMilliseconds = datediff(ms,@StartTime,CURRENT_TIMESTAMP)
SET @i += 1
END -- WHILE
SELECT DurationInMilliseconds = Duration FROM @ExecutionTime
GO
DROP TABLE #values
SET NOCOUNT OFF;
这种差异似乎只适用于对象是 B+树的情况。当删除
primary key
表变量时,它是一个堆,我得到了以下结果但是对于 PK,我在测试中也发现了类似的模式,典型的结果如下。
我的理论是,在对本地临时 B+ 树进行批量插入时,有一些优化可用,这些优化仅适用于尚未分配任何页面的情况。
我基于以下观察。
在运行各种版本的测试代码时,我只看到了这种模式
@table_variables
和#temp
表格。tempdb
也不是表中的永久##
表。为了获得较慢的性能,不必事先从表中添加和删除大量行。只需添加一行并将其保留在那里就足够了。
TRUNCATE
从表中释放所有页。DELETE
不会导致表中的最后一页被释放。使用 VS 2012 分析器表明,在更快的情况下 SQL Server 使用不同的代码路径。36% 的时间花在了较慢的情况下
sqlmin.dll!RowsetBulk::InsertRow
,而 61% 的时间花在sqlmin.dll!RowsetNewSS::InsertRow
了较慢的情况下。跑步
删除返回后
我发现通过启用跟踪标志 610可以在一定程度上减少时间差异。
这具有显着减少后续插入的日志记录数量的效果(从 350 MB 下降到 103 MB,因为它不再记录单个插入的行值),但这对第 2 次和后续情况
@table
的计时只有很小的改进#table
差距仍然存在。跟踪标志显着提高了插入其他两种表类型的一般性能。通过查看事务日志,我注意到针对空本地临时表的初始插入似乎记录得更少(96 MB)。
值得注意的是,与较慢情况下的 over 相比,这些较快的插入只有
657
事务(LOP_BEGIN_XACT
/对)。特别是操作似乎大大减少。较慢的情况对于表中的每个页面都有一个事务日志条目(大约),而在快速情况下只有这样的条目。LOP_COMMIT_XACT
10,000
LOP_FORMAT_PAGE
10,270
4
这三种情况下使用的日志如下(我已经删除了更新系统基表的日志记录以减少文本量,但它们仍然包含在总数中)
记录第一次插入
@table_var
(96.5 MB)记录后续插入 TF 610 关闭 (350 MB)
记录后续插入 TF 610 (103 MB)
观察和推测。. .
在某些系统上,CURRENT_TIMESTAMP 被定义为当前事务开始的时间。快速搜索发现没有关于 CURRENT_TIMESTAMP 在 SQL Server 上的行为的明确文档。但是SQL Server的默认模式是自动提交事务,这里没有BEGIN TRANSACTION,所以应该是INSERT语句之前的时间。(DELETE 语句应该自动提交,无论 CURRENT_TIMESTAMP 在 SQL Server 上以哪种方式工作,当您使用自动提交的事务时,它都不应该与 DELETE 语句有任何关系。)
在第一次迭代中,DELETE 语句没有任何实际工作要做,也没有任何单独的行要记录。也许优化器知道这一点,这减少了第一次迭代的时间。(没有要删除的行,也没有要记录的单个行的组合。)
您可以通过在删除之前插入来测试(我认为)。