SQL出力句が挿入されていない列を返すことは可能ですか?


123

データベースに変更を加えたため、古いデータを新しいテーブルに移行する必要があります。そのために、元のテーブル(Practice)からデータを取得してテーブル(ReportOptions)に入力し、2番目の中間テーブル(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...)

練習から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返信ありがとうございます。宛先テーブルに必要なフィールドがありません(ReportOptionにないPracticeIdが必要です)
AlejandroB。12年

回答:


191

これを行うMERGEには、代わりにinsert を使用します。

だからこれを交換

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

重要なのは、マージ検索条件でtrue(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
ありがとう、これでうまくいきます!私は偽のtempフィールドを使用するつもりでしたが、これははるかにエレガントです。
Alejandro B.

1
優れた!このトリックは一粒の金です!コレクションの最初の行に追加されました!
Vadim Loboda 2013

1
スウィート!時々バグのあるMERGEコマンドを使用する必要がないことを願っていますが、それ以外は完全にエレガントです。
タブアレマン2017年

4
警告されます。私は、過去1年間で使用量とともに成長しているというマージステートメントを使用しました。保存中にタイムアウトが発生し始めましたが、マージステートメントは常にテーブルをロックするため、4分ごとに35〜160秒のテーブルロックがありました。テーブルのロックを回避するために、挿入/更新を使用するためにいくつかのマージステートメントを再構築し、挿入/更新ごとに更新する行数を500に制限する必要があります。この非常に重要なテーブルは、1日あたり約2時間半ロックされたままであったため、保存の遅延からタイムアウトまですべてが発生したと推定します。
CubeRoot

3
また、多くの取引ブレーカーは、MERGEに未修正のバグがたくさんあり、奇妙な状況下で表面化するということです。たとえば、Aaron Bertrandによるこの記事を参照してください。マイクロソフトはそれらの一部を修正することを拒否しています。私の秘密の疑いは、MSがMS Connectサービス全体を非推奨にして、修正したくないMERGEステートメントのすべてのバグを忘れさせていることです。
リバースエンジニア、

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

主なトリックは、ソースと宛先のテーブルからの順序付けられたデータでマッピングテーブルを2回埋めることです。詳細はこちら:SQL Server 2005でOUTPUTを使用して挿入されたデータをマージする


1
これは私の問題を解決しません。私はする必要がありOutput、私のInsert「出力によTable含んIdentityターゲットから値Table以外でのInsertソースから-ed値(PK)をTable出力することを異なるバッチ)使用中の(そして(ところで、私ができるようにTable移入することColumnでを値をTable持つソースIdentity)。aがなければMerge、私は次のことを行う必要があると思います:a)開始Transaction、b)次を取得Identity、c)Table計算済みIdentityで一時に挿入、d)セットIdentity_Insert、e)一時からInsertターゲットに挿入、f)クリア。TableTableIdentity_Insert
トム
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.