เป็นไปได้หรือไม่ที่ส่วนคำสั่ง SQL Output ส่งคืนคอลัมน์ที่ไม่ได้ถูกแทรก


123

ฉันได้ทำการแก้ไขฐานข้อมูลบางอย่างและจำเป็นต้องย้ายข้อมูลเก่าไปยังตารางใหม่ สำหรับสิ่งนั้นฉันต้องกรอกข้อมูลในตาราง (ReportOptions) โดยนำข้อมูลจากตารางเดิม (แบบฝึกหัด) และเติมตารางกลางที่สอง (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...)

ฉันทำแบบสอบถามเพื่อรับข้อมูลทั้งหมดที่จำเป็นในการย้ายจาก Practice ไปยัง ReportOptions แต่ฉันมีปัญหาในการกรอกตารางกลาง

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

ถ้าฉันสามารถอ้างอิงฟิลด์ที่ไม่ได้อยู่ในตารางปลายทางในส่วนคำสั่ง OUTPUT นั่นจะดีมาก (ฉันคิดว่าทำไม่ได้ แต่ฉันไม่รู้แน่ชัด) ความคิดใด ๆ เกี่ยวกับวิธีการบรรลุความต้องการของฉัน?


1
คุณสามารถส่งคืนคอลัมน์ใด ๆ ของตารางที่คุณแทรกแถวไว้ในOUTPUTอนุประโยคของคุณ ดังนั้นแม้ว่าคุณจะไม่ได้ระบุค่าสำหรับคอลัมน์ใดคอลัมน์หนึ่งในINSERTคำสั่งของคุณคุณก็ยังสามารถระบุคอลัมน์นั้นในOUTPUTอนุประโยคได้ อย่างไรก็ตามคุณไม่สามารถส่งคืนตัวแปรหรือคอลัมน์ SQL จากตารางอื่นได้
marc_s

2
@marc_s ขอบคุณสำหรับการตอบกลับของคุณ แต่ฉันไม่มีฟิลด์ที่ฉันต้องการในตารางปลายทาง (ฉันต้องการ PracticeId ซึ่งไม่ได้อยู่ใน ReportOption)
Alejandro B.

คำตอบ:


191

คุณสามารถทำได้โดยใช้MERGEแทนการแทรก:

ดังนั้นแทนที่สิ่งนี้

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

กับ

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

กุญแจสำคัญคือการใช้เพรดิเคตที่จะไม่เป็นจริง (1 = 0) ในเงื่อนไขการค้นหาที่ผสานดังนั้นคุณจะทำการแทรกเสมอ แต่สามารถเข้าถึงฟิลด์ได้ทั้งในตารางต้นทางและตารางปลายทาง


นี่คือรหัสทั้งหมดที่ฉันใช้ในการทดสอบ:

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 

อ่านเพิ่มเติมและที่มาของสิ่งที่ฉันรู้ในเรื่องนี้อยู่ที่นี่


2
ขอบคุณนี่เป็นเคล็ดลับ! ฉันจะใช้ฟิลด์อุณหภูมิปลอม แต่มันสวยกว่ามาก
Alejandro B.

1
ยอดเยี่ยม เคล็ดลับเม็ดทอง! เพิ่มลงในแถวแรกในคอลเลกชันแล้ว!
Vadim Loboda

1
Suweet! ฉันหวังว่ามันไม่จำเป็นต้องใช้คำสั่ง MERGE ในบางครั้ง แต่มันก็ดูสง่างามอย่างสมบูรณ์แบบ
Tab Alleman

4
ขอเตือน. ฉันใช้คำสั่งผสานว่าในปีที่ผ่านมามีการใช้งานเพิ่มขึ้น เราเริ่มมีการหมดเวลาระหว่างการบันทึกและปรากฎว่าเนื่องจากคำสั่ง merge จะล็อกตารางเสมอเรามีเวลา 35-160 วินาทีในการล็อกตารางทุก 4 นาที ฉันต้องสร้างคำสั่งผสานใหม่หลายรายการเพื่อใช้การแทรก / อัปเดตและ จำกัด จำนวนแถวที่อัปเดตเป็น 500 ต่อการแทรก / อัปเดตเพื่อหลีกเลี่ยงการล็อกตาราง ฉันประเมินว่าตารางที่สำคัญมากนี้ถูกล็อคไว้เกือบ 2 1/2 ชั่วโมงต่อวันซึ่งทำให้ทุกอย่างตั้งแต่การบันทึกช้าไปจนถึงการหมดเวลา
CubeRoot

3
นอกจากนี้ตัวทำลายข้อตกลงสำหรับหลาย ๆ คนก็คือ MERGE มีข้อบกพร่องที่ไม่ได้แก้ไขจำนวนมากซึ่งอยู่ภายใต้เงื่อนไขที่แปลกประหลาด เช่นดูบทความนี้โดย Aaron Bertrand Microsoft ปฏิเสธที่จะแก้ไขบางส่วนและข้อสงสัยที่เป็นความลับของฉันคือ MS เลิกใช้บริการMS Connectทั้งหมดเพียงเพื่อพยายามทำให้เราลืมข้อบกพร่องเหล่านั้นทั้งหมดในคำสั่งผสานที่ไม่ต้องการแก้ไข
Reversed Engineer

14

บางทีคนที่ใช้MS SQL Server 2005 หรือต่ำกว่าจะพบว่าคำตอบนี้มีประโยชน์


MERGE จะใช้ได้เฉพาะกับ SQL Server 2008 หรือสูงกว่า สำหรับส่วนที่เหลือฉันพบวิธีแก้ปัญหาอื่นซึ่งจะทำให้คุณสามารถสร้างตารางการแมปได้

ความละเอียดจะเป็นอย่างไรสำหรับ 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

เคล็ดลับหลักคือเรากรอกตารางการแมปสองครั้งด้วยข้อมูลที่สั่งซื้อจากตารางต้นทางและปลายทาง สำหรับรายละเอียดเพิ่มเติมที่นี่: การรวมข้อมูลที่แทรกโดยใช้ OUTPUT ใน SQL Server 2005


1
นี่จะไม่สามารถแก้ปัญหาของฉันได้ ฉันต้องOutputฉันInsert'เพื่อส่งออกTableที่มีIdentityค่าจากเป้าหมายTableที่มีความไม่Insertคุ้มค่า -ed (PK) จากแหล่งที่มาTable(BTW ดังนั้นฉันทำได้แล้ว (ในรุ่นที่แตกต่างกัน) การใช้งานที่ส่งออกTableไปเติมColumnใน แหล่งที่มาTableพร้อมIdentityค่า) โดยไม่ต้องMerge, ฉันคิดฉันต้องก) เริ่มต้นTransactionข) ได้รับต่อไปIdentityค) แทรกเข้าไปในอุณหภูมิTableที่มีการคำนวณIdentityง) ชุดIdentity_Insertอี) Insertเข้าสู่เป้าหมายTableจากอุณหภูมิTable, ฉ) Identity_Insertที่ชัดเจน
ทอม
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.