1

我正在使用该MERGE功能将数据插入到使用批量导入表作为源的表中。(如此处所述

这是我的查询:

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; 

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

执行计划

由于这种排序成本,查询需要几秒钟才能完成。有没有办法加快插入速度?也许一些索引提示或附加索引?即使有几千个条目,这样的插入也不应该花那么长时间。

4

1 回答 1

4

我可以用以下方法重现此问题

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; 

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

计划

排序是Expr1011, Action1010从先前的运算符输出的两个计算列。

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

Action1010是指示插入、更新、删除的标志。它总是4在这种情况下,因为该MERGE语句可以执行的唯一操作是INSERT

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

在此处输入图像描述

DMLRequestSort属性是根据预期插入的行数设置的。保罗怀特在这里的评论中解释

2008 年添加了 [DMLRequestSort] 以支持最少记录 INSERT 语句的能力。最少记录的前提条件之一是行以集群键顺序呈现给 Insert 运算符。

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

如果函数getconditionalidentity以升序返回生成的标识值(这似乎是合理的),那么排序的输入将已经是所需的顺序。在这种情况下,计划中的排序在逻辑上是冗余的,(以前有一个类似的问题,即 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; 

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

于 2013-07-25T15:37:17.893 回答