You can (ab)use MERGE
with OUTPUT
clause.
MERGE
can INSERT
, UPDATE
and DELETE
rows. In our case we need only to INSERT
.
1=0 is always false, so the NOT MATCHED BY TARGET
part is always executed.
In general, there could be other branches, see docs.
WHEN MATCHED
is usually used to UPDATE
;
WHEN NOT MATCHED BY SOURCE
is usually used to DELETE
, but we don't need them here.
This convoluted form of MERGE
is equivalent to simple INSERT
,
but unlike simple INSERT
its OUTPUT
clause allows to refer to the columns that we need.
It allows to retrieve columns from both source and destination tables thus saving a mapping between old and new IDs.
MERGE INTO [dbo].[Test]
USING
(
SELECT [Data]
FROM @Old AS O
) AS Src
ON 1 = 0
WHEN NOT MATCHED BY TARGET THEN
INSERT ([Data])
VALUES (Src.[Data])
OUTPUT Src.ID AS OldID, inserted.ID AS NewID
INTO @New(ID, [OtherID])
;
Regarding your update and relying on the order of generated IDENTITY
values.
In the simple case, when [dbo].[Test]
has IDENTITY
column, then INSERT
with ORDER BY
will guarantee that the generated IDENTITY
values would be in the specified order. See point 4 in Ordering guarantees in SQL Server. Mind you, it doesn't guarantee the physical order of inserted rows, but it guarantees the order in which IDENTITY
values are generated.
INSERT INTO [dbo].[Test] ([Data])
SELECT [Data]
FROM @Old
ORDER BY [RowID]
But, when you use the OUTPUT
clause:
INSERT INTO [dbo].[Test] ([Data])
OUTPUT inserted.[ID] INTO @New
SELECT [Data]
FROM @Old
ORDER BY [RowID]
the rows in the OUTPUT
stream are not ordered. At least, strictly speaking, ORDER BY
in the query applies to the primary INSERT
operation, but there is nothing there that says what is the order of the OUTPUT
. So, I would not try to rely on that. Either use MERGE
or add an extra column to store the mapping between IDs explicitly.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…