鉴于此表:
CREATE TABLE dbo.Target (
TargetId int identity(1, 1) NOT NULL,
Color varchar(20) NOT NULL,
Action varchar(10) NOT NULL, -- of course this should be normalized
Code int NOT NULL,
CONSTRAINT PK_Target PRIMARY KEY CLUSTERED (TargetId)
);
在两个稍微不同的场景中,我想插入行并从标识列返回值。
方案 1
INSERT dbo.Target (Color, Action, Code)
OUTPUT inserted.TargetId
SELECT t.Color, t.Action, t.Code
FROM
(VALUES
('Blue', 'New', 1234),
('Blue', 'Cancel', 4567),
('Red', 'New', 5678)
) t (Color, Action, Code)
;
方案 2
CREATE TABLE #Target (
Color varchar(20) NOT NULL,
Action varchar(10) NOT NULL,
Code int NOT NULL,
PRIMARY KEY CLUSTERED (Color, Action)
);
-- Bulk insert to the table the same three rows as above by any means
INSERT dbo.Target (Color, Action, Code)
OUTPUT inserted.TargetId
SELECT t.Color, t.Action, t.Code
FROM #Target
;
问题
我是否可以依赖从dbo.Target
表插入返回的标识值按照它们在 1)VALUES
子句和 2)#Target
表中存在的顺序返回,以便我可以通过它们在输出行集中的位置将它们关联回原始输入?
以供参考
下面是一些精简的 C# 代码,演示了应用程序中发生的事情(场景 1,即将转换为使用SqlBulkCopy
):
public IReadOnlyCollection<Target> InsertTargets(IEnumerable<Target> targets) {
var targetList = targets.ToList();
const string insertSql = @"
INSERT dbo.Target (
CoreItemId,
TargetDateTimeUtc,
TargetTypeId,
)
OUTPUT
Inserted.TargetId
SELECT
input.CoreItemId,
input.TargetDateTimeUtc,
input.TargetTypeId,
FROM
(VALUES
{0}
) input (
CoreItemId,
TargetDateTimeUtc,
TargetTypeId
);";
var results = Connection.Query<DbTargetInsertResult>(
string.Format(
insertSql,
string.Join(
", ",
targetList
.Select(target => $@"({target.CoreItemId
}, '{target.TargetDateTimeUtc:yyyy-MM-ddTHH:mm:ss.fff
}', {(byte) target.TargetType
})";
)
)
)
.ToList();
return targetList
.Zip( // The correlation that relies on the order of the two inputs being the same
results,
(inputTarget, insertResult) => new Target(
insertResult.TargetId, // with the new TargetId to replace null.
inputTarget.TargetDateTimeUtc,
inputTarget.CoreItemId,
inputTarget.TargetType
)
)
.ToList()
.AsReadOnly();
}
不,如果没有实际的书面保证,您就不能依赖任何保证。该文件明确指出没有这样的保证。
这将依赖于许多未记录的假设
如果向子句
(Color, Action)
添加 600 行,则可以看到第二点失败的示例(假设 PK 的集群)。然后计划在插入之前有一个排序运算符,因此会丢失子句VALUES
中的原始顺序。VALUES
不过,有一个记录在案的方法可以实现您的目标,这是在源代码中添加编号并使用
MERGE
而不是INSERT
@a_horse_with_no_name
是的,你可以。SQL Server 中的排序保证……声明
所以你可以使用
这将保证标识值按顺序分配,
t.SourceId
但不能保证它们以任何特定顺序输出,或者分配的标识列值没有间隙(例如,如果尝试并发插入)。