Apakah mungkin untuk klausa SQL Output untuk mengembalikan kolom yang tidak disisipkan?

123

Saya telah membuat beberapa modifikasi pada database saya dan saya perlu memigrasi data lama ke tabel baru. Untuk itu, saya perlu mengisi tabel (ReportOptions) yang mengambil data dari tabel asli (Practice), dan mengisi tabel perantara kedua (PracticeReportOption).

ReportOption (ReportOptionId int PK, field1, field2...)
Practice (PracticeId int PK, field1, field2...)
PracticeReportOption (PracticeReportOptionId int PK, PracticeId int FK, ReportOptionId int FK, field1, field2...)

Saya membuat kueri untuk mendapatkan semua data yang saya butuhkan untuk berpindah dari Praktik ke ReportOptions, tetapi saya mengalami masalah untuk mengisi tabel perantara

--Auxiliary tables
DECLARE @ReportOption TABLE (PracticeId int /*This field is not on the actual ReportOption table*/, field1, field2...)
DECLARE @PracticeReportOption TABLE (PracticeId int, ReportOptionId int, field1, field2)

--First I get all the data I need to move
INSERT INTO @ReportOption
SELECT P.practiceId, field1, field2...
  FROM Practice P

--I insert it into the new table, but somehow I need to have the repation PracticeId / ReportOptionId
INSERT INTO ReportOption (field1, field2...)
OUTPUT @ReportOption.PracticeId, --> this is the field I don't know how to get
       inserted.ReportOptionId
  INTO @PracticeReportOption (PracticeId, ReportOptionId)
SELECT field1, field2
  FROM @ReportOption

--This would insert the relationship, If I knew how to get it!
INSERT INTO @PracticeReportOption (PracticeId, ReportOptionId)
SELECT PracticeId, ReportOptionId
  FROM @ReportOption

Jika saya bisa mereferensikan bidang yang tidak ada di tabel tujuan pada klausa OUTPUT, itu akan bagus (saya pikir saya tidak bisa, tapi saya tidak tahu pasti). Ada gagasan tentang bagaimana memenuhi kebutuhan saya?

Alejandro B.
sumber
1
Anda dapat mengembalikan salah satu kolom dari tabel yang barisnya Anda sisipkan, di OUTPUTklausa Anda . Jadi, meskipun Anda tidak memberikan nilai untuk kolom tertentu dalam INSERTpernyataan Anda, Anda masih dapat menentukan kolom tersebut dalam OUTPUTklausa. Namun Anda tidak dapat mengembalikan variabel atau kolom SQL dari tabel lain.
marc_s
2
@marc_s terima kasih atas balasan Anda, tetapi saya tidak memiliki bidang yang saya butuhkan di tabel tujuan (saya perlu PracticeId, yang tidak ada di ReportOption)
Alejandro B.

Jawaban:

191

Anda dapat melakukan ini dengan menggunakan MERGEalih-alih menyisipkan:

jadi gantilah ini

INSERT INTO ReportOption (field1, field2...)
OUTPUT @ReportOption.PracticeId, --> this is the field I don't know how to get
       inserted.ReportOptionId
  INTO @PracticeReportOption (PracticeId, ReportOptionId)
SELECT field1, field2
  FROM @ReportOption

dengan

MERGE INTO ReportOption USING @ReportOption AS temp ON 1 = 0
WHEN NOT MATCHED THEN
    INSERT (field1, field2)
    VALUES (temp.Field1, temp.Field2)
    OUTPUT temp.PracticeId, inserted.ReportOptionId, inserted.Field1, inserted.Field2
    INTO @PracticeReportOption (PracticeId, ReportOptionId, Field1, Field2);

Kuncinya adalah menggunakan predikat yang tidak akan pernah benar (1 = 0) dalam kondisi pencarian gabungan, jadi Anda akan selalu melakukan penyisipan, tetapi memiliki akses ke bidang di tabel sumber dan tujuan.


Berikut ini seluruh kode yang saya gunakan untuk mengujinya:

CREATE TABLE ReportOption (ReportOptionID INT IDENTITY(1, 1), Field1 INT, Field2 INT)
CREATE TABLE Practice (PracticeID INT IDENTITY(1, 1), Field1 INT, Field2 INT)
CREATE TABLE PracticeReportOption (PracticeReportOptionID INT IDENTITY(1, 1), PracticeID INT, ReportOptionID INT, Field1 INT, Field2 INT)

INSERT INTO Practice VALUES (1, 1), (2, 2), (3, 3), (4, 4)


MERGE INTO ReportOption r USING Practice p ON 1 = 0
WHEN NOT MATCHED THEN
    INSERT (field1, field2)
    VALUES (p.Field1, p.Field2)
    OUTPUT p.PracticeId, inserted.ReportOptionId, inserted.Field1, inserted.Field2
    INTO PracticeReportOption (PracticeId, ReportOptionId, Field1, Field2);

SELECT  *
FROM    PracticeReportOption

DROP TABLE ReportOption
DROP TABLE Practice
DROP TABLE PracticeReportOption 

Lebih banyak bacaan, dan sumber dari semua yang saya ketahui tentang masalah ini ada di Sini

GarethD
sumber
2
Terima kasih, ini berhasil! Saya akan menggunakan bidang temp palsu tapi ini jauh lebih elegan.
Alejandro B.
1
Luar biasa! Trik ini adalah sebutir emas! Ditambahkan ke baris pertama dalam koleksi!
Vadim Loboda
1
Suweet! Saya berharap itu tidak harus menggunakan perintah MERGE yang terkadang bermasalah, tetapi sebaliknya sangat elegan.
Tab Alleman
4
Berhati-hatilah. Saya menggunakan pernyataan gabungan yang selama setahun terakhir telah berkembang seiring penggunaan. Kami mulai mengalami batas waktu selama penyimpanan dan ternyata karena pernyataan penggabungan selalu mengunci tabel, kami mengalami penguncian tabel selama 35-160 detik setiap 4 menit. Saya harus merekonstruksi beberapa pernyataan penggabungan untuk menggunakan penyisipan / pembaruan dan membatasi jumlah baris yang mereka perbarui menjadi 500 per penyisipan / pembaruan untuk menghindari penguncian tabel. Saya memperkirakan bahwa tabel yang sangat penting ini dikunci hampir 2 1/2 jam per hari yang menyebabkan segalanya mulai dari penyimpanan lambat hingga batas waktu.
CubeRoot
3
Juga, pemecah masalah bagi banyak orang, adalah bahwa MERGE memiliki banyak bug yang tidak diperbaiki di dalamnya, yang muncul dalam kondisi yang aneh. Misalnya lihat artikel ini oleh Aaron Bertrand. Microsoft menolak untuk memperbaiki beberapa di antaranya, dan kecurigaan rahasia saya adalah bahwa MS mencabut seluruh layanan MS Connect mereka hanya untuk mencoba dan membuat kami melupakan semua bug dalam pernyataan MERGE yang tidak ingin mereka perbaiki.
Reversed Engineer
14

Mungkin seseorang yang menggunakan MS SQL Server 2005 atau yang lebih rendah akan menganggap jawaban ini berguna.


MERGE hanya akan berfungsi untuk SQL Server 2008 atau lebih tinggi. Selebihnya saya menemukan solusi lain yang akan memberi Anda kemampuan untuk membuat jenis tabel pemetaan.

Berikut tampilan Resolusi untuk SQL 2005:

DECLARE @ReportOption TABLE (ReportOptionID INT IDENTITY(1, 1), Field1 INT, Field2 INT)
DECLARE @Practice TABLE(PracticeID INT IDENTITY(1, 1), Field1 INT, Field2 INT)
DECLARE @PracticeReportOption TABLE(PracticeReportOptionID INT IDENTITY(1, 1), PracticeID INT, ReportOptionID INT, Field1 INT, Field2 INT)

INSERT INTO @Practice (Field1, Field2) VALUES (1, 1)
INSERT INTO @Practice (Field1, Field2) VALUES (2, 2)
INSERT INTO @Practice (Field1, Field2) VALUES (3, 3)
INSERT INTO @Practice (Field1, Field2) VALUES (4, 4)

INSERT INTO @ReportOption (field1, field2)
    OUTPUT INSERTED.ReportOptionID, INSERTED.Field1, INSERTED.Field2 INTO @PracticeReportOption (ReportOptionID, Field1, Field2)
    SELECT Field1, Field2 FROM @Practice ORDER BY PracticeID ASC;


WITH CTE AS ( SELECT PracticeID, ROW_NUMBER() OVER ( ORDER BY PracticeID ASC ) AS ROW FROM @Practice )
UPDATE M SET M.PracticeID = S.PracticeID 
    FROM @PracticeReportOption AS M
    JOIN CTE AS S ON S.ROW = M.PracticeReportOptionID

    SELECT * FROM @PracticeReportOption

Trik utamanya adalah kita mengisi tabel pemetaan dua kali dengan data yang dipesan dari tabel Sumber dan tujuan. Untuk detail selengkapnya di sini: Menggabungkan Data yang Disisipkan Menggunakan OUTPUT di SQL Server 2005

Val
sumber
1
Ini tidak akan menyelesaikan masalah saya. Saya perlu Outputsaya Insert's untuk output Tableyang mencakup Identitynilai dari target Tabledengan non Insertnilai -ed (PK) dari sumber Table(btw, jadi saya kemudian bisa (dalam Batch yang berbeda) menggunakan output Tableuntuk mengisi Columndi sumber Tabledengan Identitynilai). Tanpa a Merge, saya membayangkan saya harus: a) mulai Transaction, b) mendapatkan berikutnya Identity, c) Masukkan ke dalam temp Tabledengan dihitung Identity, d) set Identity_Insert, e) Insertke dalam target Tabledari temp Table, f) jelas Identity_Insert.
Tom