[MSSQL] MERGE 문이 제대로 작성된게 맞을까요? 1 10 1,736

by 곰장어 [2021.02.05 10:25:51]


목표:

PRIMARY KEY = GPI001 + GPI002

임시 테이블(S라 칭함)의 GPI001,GPI002 컬럼이

원본 테이블(T라 칭함)의 GPI001,GPI002 컬럼값과 서로 일치하고,  

S와 T의 GPI003,GPI004,GPI005의 값이 서로 다를 경우

T의 GPI003,GPI004,GPI005 값을 S의 GPI003,GPI004,GPI005 의 값으로 UPDATE한다.

T의 GPI006 값은 컬럼값이 업데이트 되었을경우 기존값과 업데이트 후 의 값을 나타내준다.

-------------UPDATE 종료--------------

임시테이블의 GPI001,GPI002 값이 원본테이블의 GPI001,GPI002 에 존재하지 않을경우

원본테이블에 임시테이블의 GPI001 ~ GPI007 까지의 값을 INSERT 한다.

-------------INSERT 종료 ---------------

원본테이블의 GPI001,GPI002 값이 임시테이블 GPI001,GPI002에 존재하지않을경우 

원본테이블 GPI007 값을 '단종' 으로 UPDATE 한다.

 

작성쿼리:

MERGE Into tableName AS T
                                  USING Temp_tableName AS S
                                     ON T.GPI001 = S.GPI001
                                    AND T.GPI002 = S.GPI002
									AND  (T.GPI003 <> S.GPI003 OR T.GPI004 <> S.GPI004 OR T.GPI005 <> S.GPI005)
                                    WHEN MATCHED THEN
                                 UPDATE 
                                  SET T.GPI003 = S.GPI003
                                    , T.GPI004 = S.GPI004
                                    , T.GPI005 = S.GPI005
                                    , T.GPI006 = (CASE WHEN T.GPI003 <> S.GPI003 THEN '[상품명]' + T.GPI003 + '→' + S.GPI003 + ' ; ' ELSE '' END +
                                                  CASE WHEN T.GPI004 <> S.GPI004 THEN '[구매가]' + T.GPI004 + '→' + S.GPI004 + ' ; ' ELSE '' END +
                                                  CASE WHEN T.GPI05 <> S.GPI05THEN '[품절여부]' + T.GPI05 + '→' + S.GPI05 + ' ; ' ELSE '' END);

                                      INSERT INTO tableName(GPI001,GPI002,GPI003,GPI004,GPI005,GPI006,GPI007)
                                        SELECT S.GPI001,S.GPI002,S.GPI003,S.GPI004,S.GPI005,S.GPI006,'신규'
										  FROM Temp_tableName S
										  WHERE NOT EXISTS
										    (SELECT 'X' 
											FROM tableName
											WHERE GPI001 = S.GPI001
											  AND GPI002 = S.GPI002);

                                     UPDATE T SET T.GPI007 ='단종'
                                        FROM tableName T
                                     WHERE NOT EXISTS 
                                        (SELECT 'X'
                                         FROM Temp_tableName
                                         WHERE GPI001 = T.GPI001
                                           AND GPI002 = T.GPI002);

 

C# 반복문 내에서 한 레코드씩 DB에 쿼리를 수행하다보니 너무 느려서 위처럼 한번에 MERGE문을 사용하려고 하는데요,

개선할 부분이 있을지 조언주신다면 감사하겠습니다

그리고 위처럼 작성을 하면 MERGE문의 범위가 맨밑의 단종처리를 해주는 쿼리까지 포함되는게 맞을까요?

by 마농 [2021.02.05 13:50:19]

MERGE 문을 UPDATE 용으로만 사용하고, INSERT 문을 별개로 수행하고 있고,
추가 UPDATE 구문까지 총 3개 구문이 실행되네요.
MERGE 구문 하나로 가능합니다.
 

MERGE INTO tableName t
USING Temp_tableName s
   ON t.gpi001 = s.gpi001
  AND t.gpi002 = s.gpi002
 WHEN MATCHED AND t.gpi003 != s.gpi003
              AND t.gpi004 != s.gpi004
              AND t.gpi005 != s.gpi005
      THEN
      UPDATE 
         SET gpi003 = s.gpi003
           , gpi004 = s.gpi004
           , gpi005 = s.gpi005
           , gpi006 = CASE WHEN t.gpi003 <> s.gpi003 THEN '[상품명]'   + t.gpi003 + '→' + s.gpi003 + ' ; ' ELSE '' END
                    + CASE WHEN t.gpi004 <> s.gpi004 THEN '[구매가]'   + t.gpi004 + '→' + s.gpi004 + ' ; ' ELSE '' END
                    + CASE WHEN t.gpi005 <> s.gpi005 THEN '[품절여부]' + t.gpi005 + '→' + s.gpi005 + ' ; ' ELSE '' END
           , gpi007 = '변경'
 WHEN NOT MATCHED THEN
      INSERT (gpi001, gpi002, gpi003, gpi004, gpi005, gpi006, gpi007)
      VALUES (s.gpi001, s.gpi002, s.gpi003, s.gpi004, s.gpi005, '', '신규')
 WHEN NOT MATCHED BY SOURCE THEN
      UPDATE 
         SET gpi007 = '단종'
;

 


by 곰장어 [2021.02.07 22:54:38]

감사합니다!!!


by 곰장어 [2021.02.10 02:02:33]
MERGE INTO tableName t
USING Temp_tableName s
   ON t.gpi001 = s.gpi001
  AND t.gpi002 = s.gpi002
 WHEN MATCHED AND (t.gpi003 != s.gpi003
              OR t.gpi004 != s.gpi004
              OR t.gpi005 != s.gpi005)
      THEN UPDATE...

개선해 주신 위 코드와 

MERGE INTO tableName t
USING Temp_tableName s
   ON t.gpi001 = s.gpi001
  AND t.gpi002 = s.gpi002
  AND (t.gpi003 != s.gpi003
       OR t.gpi004 != s.gpi004
       OR t.gpi005 != s.gpi005)
 WHEN MATCHED THEN...

제가 작성한 기존 코드랑 비교했을떄

AND (t.gpi003 != s.gpi003
       OR t.gpi004 != s.gpi004
       OR t.gpi005 != s.gpi005)

이부분을 WHEN MATCHED 위에 넣느냐(제가 작성한) 

WHEN MATCHED 뒤에 AND로 연결하느냐(마농님께서 작성하신) 의 

차이가 있을까요?

 

그리고 맨 밑 라인에서 SOURCE 와 매치되지않을때 수행하는  

UPDATE SET GPI007 ='단종'  을 아래처럼 수정하려고하는데요,

UPDATE SET GPI007 ='단종' , T.GPI008 = T.GPI007 + '→' + S.GPI0007;

이렇게 GPI008 컬럼값을

기존에 gpi007 값이 판매 였을경우 판매 → 단종 이런식으로 UPDATE 해주려고 하는데

수행시 [여러 부분으로 구성된 식별자 "S.GPI007"은(는) 바인딩할 수 없습니다.]

라는 에러가 발생되는데 검색해보니 테이블 별칭을 지정해주지 않을경우에 발생되는 에러라고 하더라구요

그런데 위에서 별칭을 S로 지정해주었는데도 왜 뜨는지 원인을 잘모르겠네요..ㅠ

혹시 조언을 주신다면 정말 감사하겠습니다


by 마농 [2021.02.19 10:33:22]

조인조건이 되느냐, 필터조건이 되느냐 차이로.

아래 NOT MATCHED BY SOURCE 및 NOT MATCHED 구문의 적용 범위에 영향을 끼치게 됩니다.

when not matched by source 구문에서는 타겟 정보만 있습니다. s 는 없습니다.


by 곰장어 [2021.02.19 11:16:03]

그럼 GPI007 이 만약 '판매' 에서 '단종' 으로 변경됐을때 GPI008에 '판매 -> 단종' 으로 값을 넣어주려면

when not matched by source 를 사용하지않고 쿼리를 별도로 분리해야하는건가요?

답변 감사드립니다


by 마농 [2021.02.19 12:04:15]
  WHEN NOT MATCHED BY SOURCE THEN
  UPDATE
     SET gpi007 = '단종'
       , gpi008 = gpi007 + '->' + '단종'

 


by 곰장어 [2021.02.23 09:52:46]

감사합니다!


by 곰장어 [2021.02.23 10:11:25]

답변주신 내용중에 조인조건,필터조건의 차이가 

아래 제가 이해한 내용이 맞을까요??

 

1. 조인조건 (003~005가 WHEN 앞에 옴)

001~002만 일치하면 UPDATE 문을 수행하고 WHEN NOT MATCHED는 001~002가 다르면 수행
위에서 001~002가 다르면 UPDATE문이 수행되지만 해당 UPDATE문 안에서도 003~005가 달라야 아래 THEN UPDATE 구문이 수행됨
따라서 만약 003~005가 다르지 않으면 UPDATE문이 수행되더라도 실질적으로 변경되는 내용은 없음

2. 필터조건 (003~005가 WHEN 뒤에 옴)

001~005가 모두 같아야  UPDATE 문을 수행하고
아래 WHEN NOT MATCHED는 001~005가 모두 달라야 수행됨


by 마농 [2021.02.23 11:41:19]

사용하신 표현이 이상하고 이해하기 어렵네요.
조인 조건에서는 키값(1,2)만 비교하면 되고
키값 비교 일치 건중에서 업데이트 할지 말지를 필터링 조건(3,4,5)으로 추가하는 형태입니다.
즉, 필터조건으로 사용되어야 할 (3,4,5) 가 조인 조건으로 사용되게 되면 결과가 잘못되게 됩니다.


by 곰장어 [2021.02.25 10:48:56]

네 이해가되었습니다 감사합니다~!

댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입