Eri*_*ikE 22 sql-server insert identity sql-server-2012 bulk-insert
鉴于此表:
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)
);
Run Code Online (Sandbox Code Playgroud)
在两个稍微不同的场景中,我想插入行并从标识列返回值。
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)
;
Run Code Online (Sandbox Code Playgroud)
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
;
Run Code Online (Sandbox Code Playgroud)
我是否可以依赖从dbo.Target表插入返回的标识值以它们在 1)VALUES子句和 2)#Target表中存在的顺序返回,以便我可以通过它们在输出行集中的位置将它们关联回原始输入?
下面是一些精简的 C# 代码,用于演示应用程序中发生的情况(场景 1,即将转换为使用SqlBulkCopy):
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)
);
Run Code Online (Sandbox Code Playgroud)
Mar*_*ith 26
我可以依赖从 dbo.Target 表插入返回的标识值以它们在 1) VALUES 子句和 2) #Target 表中存在的顺序返回,以便我可以通过它们在输出行集中的位置来关联它们到原始输入?
不,如果没有实际的书面保证,您不能依赖任何保证。文档明确指出没有这样的保证。
SQL Server 不保证使用 OUTPUT 子句的 DML 语句处理和返回行的顺序。由应用程序包含一个适当的 WHERE 子句来保证所需的语义,或者理解当多行可能符合 DML 操作的条件时,没有保证的顺序。
这将依赖于许多未记录的假设
(Color, Action)如果向该VALUES子句添加 600 行,则可以看到第二点失败的示例(假设集群 PK 为)。然后该计划在插入之前有一个排序运算符,因此在VALUES子句中丢失了原始顺序。
有一种实现目标的记录方法,这是向源添加编号并使用MERGE而不是INSERT
MERGE dbo.Target
USING (VALUES (1, 'Blue', 'New', 1234),
(2, 'Blue', 'Cancel', 4567),
(3, 'Red', 'New', 5678) ) t (SourceId, Color, Action, Code)
ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (Color,
Action,
Code)
VALUES (Color,
Action,
Code)
OUTPUT t.SourceId,
inserted.TargetId;
Run Code Online (Sandbox Code Playgroud)
合并真的有必要吗?你就不能做一个
insert into ... select ... from (values (..)) t (...) order by sourceid吗?
是的,你可以。SQL Server 中的排序保证......指出
使用 SELECT 和 ORDER BY 来填充行的 INSERT 查询保证了标识值的计算方式,但不保证行插入的顺序
所以你可以使用
INSERT dbo.Target (Color, Action, Code)
OUTPUT inserted.TargetId
SELECT t.Color, t.Action, t.Code
FROM
(VALUES (1, 'Blue', 'New', 1234),
(2, 'Blue', 'Cancel', 4567),
(3, 'Red', 'New', 5678) ) t (SourceId, Color, Action, Code)
ORDER BY t.SourceId
Run Code Online (Sandbox Code Playgroud)
这将保证标识值按顺序分配t.SourceId但不以任何特定顺序输出,或者分配的标识列值没有间隙(例如,如果尝试并发插入)。