Sử dụng các cột nguồn trong mệnh đề OUTPUT INTO của câu lệnh INSERT (SQL Server)


15

Tôi đang viết một câu lệnh chèn xử lý hàng loạt và muốn sử dụng bảng tạm thời để theo dõi ID được chèn thay vì tự lặp qua các mục và gọi SCOPE_IDENTITY () cho mỗi hàng được chèn.

Dữ liệu cần được chèn có ID (tạm thời) liên kết nó với dữ liệu khác cũng nên được chèn vào một bảng khác, vì vậy tôi cần một tham chiếu chéo của Id thực tế và Id tạm thời.

Đây là một ví dụ về những gì tôi có cho đến nay:

-- 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

Vấn đề là tôi không thể nhận được mệnh đề OUTPUT INTO để chấp nhận ID, tôi đã thử @MyInsertData.IDvà các thủ thuật khác tham gia vào bảng, nhưng dường như không có gì hoạt động.

Câu trả lời:


27

Trên thực tế, bạn có thể đạt được điều tương tự bằng cách thay đổi INSERTthành a MERGE. Mặc dù MERGEcâu lệnh thực sự là một cách khá gọn gàng để thực hiện "uperts" trong SQL Server, nhưng không có gì ngăn bạn sử dụng nó chỉ với mục đích chèn:

-- 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

Một trong những điều thú vị MERGElà nó cho phép bạn truy cập các cột nguồn cũng như các bảng dựng sẵn inserteddeletedcác bảng trong OUTPUTmệnh đề.

Mã của tôi có thể chứa lỗi, vì tôi chưa thực sự kiểm tra nó. Bài đăng trên blog của tôi từ một vài năm trước đi vào chi tiết hơn một chút, bao gồm cả về hiệu suất truy vấn.


Thật tuyệt! Lần này tôi sẽ phải theo một vòng lặp vì cần có hỗ trợ cho SQL Server 2005. Tuy nhiên tôi sẽ ghi nhớ điều này cho các dự án trong tương lai. Cảm ơn!
Louis Bolog

3
Rực rỡ, đây nên là câu trả lời được chấp nhận.
Chris Peacock

3
Mong muốn điều này là trong stackoverflow thay vì dba stackexchange. Nó có quá ít tầm nhìn. Câu trả lời tuyệt vời.
Lordbalmon

3
Làm việc như một cơ duyên từ lần thử đầu tiên ... câu trả lời tuyệt vời!
BeemerGuy

5

Mệnh đề đầu ra chỉ có thể truy cập dữ liệu trong các hàng đích và hằng / biến, chứ không phải dữ liệu từ nơi khác trong nguồn SELECT, giống như khi bạn đang vận hành trong trình kích hoạt.

https://docs.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql trạng thái:

Bất kỳ tham chiếu nào đến các cột trong bảng đang được sửa đổi phải đủ tiêu chuẩn với tiền tố XÁC NHẬN hoặc XÓA.

Vì vậy, để có được ID gốc, bạn cần đưa nó vào bảng đích để mệnh đề đầu ra có thể lặp lại, như vậy:

-- 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

mặc dù việc thay đổi lược đồ của đối tượng đích có thể không thực tế trong tình huống của bạn nên điều này có thể không được áp dụng.


3
Đó là một sự thất vọng. Mà ám mệnh đề đầu ra vô dụng cho kịch bản của tôi, trừ khi có một cột thứ 2 có thể được sử dụng như một chìa khóa :-( Oh, tốt, ít nhất tôi có thể ngừng cố gắng và tiếp tục với vòng lặp Cảm ơn..
Louis Somers
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.