Использование исходных столбцов в предложении OUTPUT INTO оператора INSERT (SQL Server)

16

Я пишу оператор вставки для пакетной обработки и хотел бы использовать временную таблицу для отслеживания вставленных идентификаторов вместо того, чтобы самому циклически проходить по элементам и вызывать SCOPE_IDENTITY () для каждой вставленной строки.

Данные, которые необходимо вставить, имеют (временные) идентификаторы, связывающие их с другими данными, которые также должны быть вставлены в другую таблицу, поэтому мне нужна перекрестная ссылка на фактический идентификатор и временный идентификатор.

Это пример того, что я имею до сих пор:

-- The existing table 
DECLARE @MyTable TABLE (ID INT IDENTITY(1,1), [Name] NVARCHAR(MAX));

-- My data I want to insert
DECLARE @MyInsertData TABLE (ID INT, [Name] NVARCHAR(MAX));
INSERT INTO @MyInsertData ( ID,Name)
VALUES ( -1 , 'bla'),(-2,'test'),(-3,'last');

DECLARE @MyCrossRef TABLE ([NewId] INT, OldId INT);

INSERT INTO @MyTable ( [Name] )
   OUTPUT Inserted.ID, INS.ID INTO @MyCrossRef
   SELECT [NAME] FROM @MyInsertData INS

-- Check the result
SELECT * FROM @MyCrossRef

Проблема в том, что я не могу заставить предложение OUTPUT INTO принять ID, я пробовал @MyInsertData.IDи другие приемы, соединяющие таблицу с самим собой, но, похоже, ничего не работает.

Луи Сомерс
источник

Ответы:

27

На самом деле, вы можете достичь того же, изменив свой INSERTна MERGE. Несмотря на то, что это MERGEзаявление - довольно хороший способ сделать «upserts» в SQL Server, ничто не мешает вам использовать его только для вставки:

-- The existing table 
DECLARE @MyTable TABLE (ID INT IDENTITY(1,1), [Name] NVARCHAR(MAX));

-- My data I want to insert
DECLARE @MyInsertData TABLE (ID INT, [Name] NVARCHAR(MAX));
INSERT INTO @MyInsertData ( ID,Name)
VALUES ( -1 , 'bla'),(-2,'test'),(-3,'last');

DECLARE @MyCrossRef TABLE ([NewId] INT, OldId INT);

MERGE INTO @MyTable AS dest
USING @MyInsertData AS ins ON 1=0   -- always false

WHEN NOT MATCHED BY TARGET          -- happens for every row, because 1 is never 0
    THEN INSERT ([Name])
         VALUES (ins.[NAME])

OUTPUT inserted.ID, ins.ID
INTO @MyCrossRef (NewId, OldId);

-- Check the result
SELECT * FROM @MyCrossRef

Одна из хороших вещей о MERGEтом , что он позволяет получить доступ к столбцам источника , а также встроенным insertedи deletedтаблицы в OUTPUTпредложении.

Мой код может содержать ошибки, поскольку я на самом деле не проверял его. В моем блоге, опубликованном несколько лет назад, немного подробнее, в том числе о производительности запросов.

Даниэль Хутмахер
источник
Замечательно! На этот раз мне придется придерживаться цикла, потому что требуется поддержка SQL Server 2005. Однако я буду помнить это для будущих проектов. Спасибо!
Луи Сомерс
3
Гениально, это должен быть принятый ответ.
Крис Пикок
3
Хотелось бы, чтобы это было в stackoverflow вместо dba stackexchange. У него слишком мало видимости. Удивительный ответ.
Lordbalmon
3
Сработал как шарм с первой попытки ... отличный ответ!
BeemerGuy
5

Предложение output может обращаться только к данным в целевых строках и константах / переменных, но не к данным из другого источника SELECT, например, если вы работали в триггере.

https://docs.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql заявляет:

Любая ссылка на столбцы в изменяемой таблице должна быть квалифицирована с префиксом INSERTED или DELETED.

Таким образом, чтобы получить исходный идентификатор, вам нужно будет включить его в таблицу назначения, чтобы предложение output могло вернуть его обратно, например так:

-- The existing table 
DECLARE @MyTable TABLE (ID INT IDENTITY(1,1), [Name] NVARCHAR(MAX), SourceID INT);

-- My data I want to insert
DECLARE @MyInsertData TABLE (ID INT, [Name] NVARCHAR(MAX));
INSERT INTO @MyInsertData ( ID,Name)
VALUES ( -1 , 'bla'),(-2,'test'),(-3,'last');

DECLARE @MyCrossRef TABLE ([NewId] INT, OldId INT);

INSERT INTO @MyTable ( [Name], SourceID )
   OUTPUT Inserted.ID, Inserted.SourceID INTO @MyCrossRef
   SELECT [NAME], ID FROM @MyInsertData INS

-- Check the result
SELECT * FROM @MyCrossRef

хотя изменение схемы целевого объекта может быть непрактичным в вашей ситуации, поэтому это может быть неприменимо.

Дэвид Спиллетт
источник
3
Это довольно разочарование. Это делает предложение вывода бесполезным для моего сценария, если только 2-й столбец не может быть использован в качестве ключа :-( Ну, по крайней мере, я могу прекратить попытки и
Louis Somers