SQL 출력 절이 삽입되지 않은 열을 반환 할 수 있습니까?


123

데이터베이스를 약간 수정했으며 이전 데이터를 새 테이블로 마이그레이션해야합니다. 이를 위해 원래 테이블 (Practice)에서 데이터를 가져 오는 테이블 (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 감사합니다,하지만 난 대상 테이블에서 필요한 필드가없는 (나는 ReportOption에없는 PracticeId 필요)
알레한드로 B.

답변:


191

MERGEinsert 대신 사용하여이 작업을 수행 할 수 있습니다 .

그래서 이것을 바꾸십시오

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

1
멋지다! 가끔 버그가있는 MERGE 명령을 사용할 필요가 없었 으면 좋겠지 만 그렇지 않으면 완벽하게 우아합니다.
Tab Alleman

4
경고 받다. 작년에 사용량이 증가한 병합 문을 사용했습니다. 저장하는 동안 시간 초과가 발생하기 시작했고 merge 문이 항상 테이블을 잠그기 때문에 4 분마다 35-160 초의 테이블 잠금이 발생했습니다. 삽입 / 업데이트를 사용하고 테이블 잠금을 피하기 위해 삽입 / 업데이트 당 업데이트되는 행 수를 500 개로 제한하기 위해 여러 병합 문을 재구성해야합니다. 이 매우 중요한 테이블이 하루에 거의 2 시간 반에 잠긴 상태로 유지되어 느린 저장에서 시간 초과에 이르기까지 모든 것이 발생했다고 추정합니다.
CubeRoot

3
또한 MERGE에는 이상한 조건에서 표면화되는 수정되지 않은 버그가 많이 포함되어 있습니다. 예를 들어 Aaron Bertrand 의이 기사 를 참조하십시오 . 마이크로 소프트는 그들 중 일부를 고치기를 거부하고 있으며, 제 비밀 의심은 MS 가 고치지 않으려는 MERGE 문에있는 모든 버그를 잊게하려고 전체 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

주요 트릭은 소스 및 대상 테이블의 순서가 지정된 데이터로 매핑 테이블을 두 번 채우는 것입니다. 자세한 내용은 여기 : SQL Server 2005에서 OUTPUT을 사용하여 삽입 된 데이터 병합


1
이것은 내 문제를 해결하지 못할 것입니다. 소스 (btw ) 의 비 -ed 값 (PK)이 있는 대상 의 값 을 포함 하는 출력 에 OutputInsert's 가 필요 하므로 다음 (다른 배치에서) 해당 출력 을 사용 하여 소스 와 값). a가 없으면 a ) start , b) get next , c) computed , d) set , e) temp from target , f) clear 로 temp에 삽입 해야 합니다. TableIdentityTableInsertTableTableColumnTableIdentityMergeTransactionIdentityTableIdentityIdentity_InsertInsertTableTableIdentity_Insert
Tom
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.