Menggunakan kolom sumber dalam klausa OUTPUT INTO dari pernyataan INSERT (SQL Server)


16

Saya menulis pernyataan penyisipan pemrosesan batch dan ingin menggunakan tabel temp untuk melacak ID yang dimasukkan alih-alih mengulang-ulang item sendiri dan memanggil SCOPE_IDENTITY () untuk setiap baris yang dimasukkan.

Data yang perlu dimasukkan memiliki (sementara) ID yang menautkannya ke data lain yang juga harus dimasukkan ke tabel lain, jadi saya perlu referensi silang dari Id aktual dan Id sementara.

Ini adalah contoh dari apa yang saya miliki sejauh ini:

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

Masalahnya adalah bahwa saya tidak bisa mendapatkan klausa OUTPUT INTO untuk menerima ID, saya sudah mencoba @MyInsertData.IDdan trik lain bergabung dengan tabel untuk dirinya sendiri, tetapi sepertinya tidak ada yang berhasil.

Jawaban:


27

Sebenarnya, Anda dapat mencapai hal yang sama dengan mengubah Anda INSERTmenjadi MERGE. Sementara MERGEpernyataan itu sebenarnya cara yang cukup rapi untuk melakukan "upserts" di SQL Server, tidak ada yang menghentikan Anda menggunakannya hanya untuk tujuan memasukkan:

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

Salah satu hal yang menyenangkan tentang MERGEini adalah memungkinkan Anda untuk mengakses kolom sumber serta built-in inserteddan deletedtabel di OUTPUTklausa.

Kode saya mungkin mengandung kesalahan, karena saya belum benar-benar mengujinya. Posting blog saya dari beberapa tahun yang lalu masuk ke sedikit lebih detail, termasuk pada kinerja permintaan.


Itu hebat! Kali ini saya harus tetap berpegang teguh karena diperlukan dukungan untuk SQL Server 2005. Namun saya akan mengingat ini untuk proyek masa depan. Terima kasih!
Louis Somers

3
Cemerlang, ini harus menjadi jawaban yang diterima.
Chris Peacock

3
Berharap ini berada di stackoverflow bukan dba stackexchange. Visibilitasnya terlalu sedikit. Jawaban yang luar biasa.
Lordbalmon

3
Bekerja seperti pesona dari percobaan pertama ... jawaban yang luar biasa!
BeemerGuy

5

Klausa output hanya dapat mengakses data di baris target dan konstanta / variabel, bukan data dari tempat lain di sumber SELECT, seperti jika Anda beroperasi di pemicu.

https://docs.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql menyatakan:

Setiap referensi ke kolom dalam tabel yang diubah harus memenuhi syarat dengan awalan INSERTED atau DELETED.

Jadi untuk mendapatkan ID asli, Anda harus memasukkannya dalam tabel tujuan agar klausa keluaran dapat mengulanginya, seperti:

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

meskipun mengubah skema objek target mungkin tidak praktis dalam situasi Anda sehingga ini mungkin tidak berlaku.


3
Itu cukup mengecewakan. Itu membuat klausa keluaran tidak berguna untuk skenario saya kecuali ada kolom 2 yang dapat digunakan sebagai kunci :-( Oh, well, setidaknya saya bisa berhenti mencoba dan melanjutkan perulangan. Terima kasih.
Louis Somers
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.