合并操作的高排序成本

时间:2013-07-24 08:08:54

标签: sql-server sql-server-2012

我使用MERGE功能使用批量导入表作为源将数据插入表中。 (as described here

这是我的疑问:

DECLARE @InsertMapping TABLE (BulkId int, TargetId int);
MERGE dbo.Target T
USING dbo.Source S
ON 0=1 WHEN NOT MATCHED THEN 
    INSERT (Data) VALUES (Data)
OUTPUT S.Id BulkId, inserted.Id INTO @InsertMapping; 

通过显示实际执行计划来评估性能时,我发现主键索引上有一个高成本排序。我不明白,因为主键应该已经按升序排序,不需要额外的排序。

Execution Plan!

由于此排序成本,查询需要几秒钟才能完成。有没有办法加快插入?也许某些指数暗示或其他指数?即使有几千个条目,这样的插入也不应该花那么长时间。

1 个答案:

答案 0 :(得分:2)

我可以使用以下

重现此问题
CREATE TABLE dbo.TargetTable(Id int IDENTITY PRIMARY KEY, Value INT)
CREATE TABLE dbo.BulkTable(Id int IDENTITY PRIMARY KEY, Value INT)

INSERT INTO dbo.BulkTable
SELECT TOP (1000000) 1
FROM   sys.all_objects o1, sys.all_objects o2

DECLARE @TargetTableMapping TABLE (BulkId   INT,TargetId INT);

MERGE dbo.TargetTable T
USING dbo.BulkTable S
ON 0 = 1
WHEN NOT MATCHED THEN 
  INSERT (Value)
  VALUES (Value)
OUTPUT S.Id AS BulkId,
       inserted.Id AS TargetId
INTO @TargetTableMapping; 

这给出了一个在聚集索引合并运算符之前进行排序的计划。

Plan

排序在Expr1011, Action1010上,它们都是先前运算符输出的计算列。

Expr1011是调用内部和未记录的函数getconditionalidentityid中的标识列生成TargetTable列的结果。

Action1010是一个表示插入,更新,删除的标志。在这种情况下始终为4,因为此MERGE语句可以执行的唯一操作是INSERT

排序在计划中的原因是聚集索引合并运算符设置了DMLRequestSort属性。

enter image description here

DMLRequestSort属性是根据预期要插入的行数设置的。保罗怀特解释in the comments here

  添加了

[DMLRequestSort]以支持最小化日志的能力   2008年INSERT语句。最小化的前提条件之一   记录是将行呈现给Insert运算符   群集密钥顺序。

无论如何,以聚簇索引键顺序插入表可以更有效,因为它可以减少随机IO和碎片。

如果函数getconditionalidentity按升序返回生成的标识值(看起来似乎合理),那么排序的输入将已按所需顺序排列。在这种情况下,计划中的排序在逻辑上是多余的(以前有similar issue与NEWSEQUENTIALID有不必要的排序)

可以通过使表达式更加不透明来消除排序。

DECLARE @TargetTableMapping TABLE (BulkId   INT,TargetId INT);
DECLARE @N BIGINT = 0x7FFFFFFFFFFFFFFF

MERGE dbo.TargetTable T
USING (SELECT TOP(@N) * FROM dbo.BulkTable) S
ON 1=0
WHEN NOT MATCHED THEN 
  INSERT (Value)
  VALUES (Value)

OUTPUT S.Id AS BulkId,
       inserted.Id AS TargetId
INTO @TargetTableMapping; 

这会减少估计的行数,并且计划不再有排序。您需要测试此实际是否可以提高性能。可能会让事情变得更糟。