/


바이 패스 문을 머지 문으로 변경 하자. 


*목표*

   프로시져 에서 바이패스 문으로 작성된 UPDATE 문 3개를  머지문을 이용 하여 사용 할수 있도록 하라. 

   업데이트 문이 기본적으로 수정을 목표로 한다면 머지 문은 INSERT DELETE UPDATE  를 하나의 쿼리 내에서 실행 할수 잇는 개념으로 보면 될듯 하다.  


왜그래야하징? 

보통 
update(select ~) 

where col='a'

set a.d=b.d

여기서 (select ~) 안에는  join 등을 이용 하여 여러개의 테이블을 검색 하여 검색된 컬럼 내에서 의 값들을 검색된 컬럼으로 setting 한다. 


형식의 업데이트 사용. 

위의 방식은 옳지 않다 그 이유는 ? 

Undocumented HIT (잘못되어도 오라클에서 보장 하지 않음)    - 보장을 하지 않는 단다. 오라클이. .. 휴 (신상진 멍청이)
 
일반적으로 특정 테이블을 Update하기 위해서는 WHERE절에 EXISTS 또는 IN 등의 Sub-Query로 조인 조건을 만족하는
Row를 먼저 Check하고, 조건을 만족하는 Row에 대하여 SET 절에서 필요한 데이터를 검색하여 Update하는 것이 보통이다.
 
이 때, Update 해야 하는 Row가 많을 경우 WHERE절이나 SET절에서 테이블을 반복적으로 Random 액세스해야 하는 부담이
발생하므로 처리 범위가 넓은 Update문의 경우에는 'Updatable Join View'를 활용할 필요가 있다.
 
이 때, 조인되는 2개의 테이블은 반드시 1:1 또는 1:M의 관계여야 하며,
Update되는 컬럼의 테이블은 M쪽 집합이어야 한다.
이것은 1쪽 집합인 Parent table의 조인 컬럼이 UK 또는 PK로 설정되어 있어야 함을 의미한다. 
이 조건을 만족하지 못하는 Updatable Join View는 에러를 Return하며 실행되지 않는다.
(ORA-01779 cannot modify a column which maps to a non key-preserved table)
 
그러나, 일반적으로 View 또는 2개 이상의 테이블을 조인한 집합을 엑세스하는
경우가 많으므로 위의 UK나 PK Constraint를 설정하기 어려운 것이 현실이다.
 
따라서, 이러한 Constraint를 피해서 Updatable Join View를 사용할 수 있도록

BYPASS_UJVC 라는 힌트를 사용하여 튜닝할 수 있다.

update /*+ BYPASS_UJVC */
(select ~) 

where col='a'

set a.d=b.d

 

이런식으로. 말이다. 하지만 11g 이상부터는 오라클은 BYPASS_UJVC 을 지원 하지 않는다 . 따라서 merge 문을 이용 하여 

복잡한 update 문을 수행 해야 한다. 


MERGE INTO table_1 A

USING( SELECT ~  ) B

ON( A.SEQ=B.SEQ)     -- 반드시 이퀄 조인 이어야만 한다. B 의 SELECT 문은 상관 없다. 

WHEN MATCHED THEN 

SET A.DD = B.CC 

WHERE COL='a'


형식 으로 하면 된다. 


많은 데이터를 할경우 오라클 튜닝 힌트가 필요 하다. 







CREATE OR REPLACE PROCEDURE IBS.SP_ACT_BS ( P_BUSI_PLC_CD IN VARCHAR2,

                                            P_FRM_DT  IN VARCHAR2,

                                            P_TO_DT   IN VARCHAR2)

IS

V_BACK_FRM_DT   VARCHAR(20) :=SUBSTR(P_FRM_DT,0,4)-1||'0101';

V_BACK_TO_DT    VARCHAR(20) :=SUBSTR(P_FRM_DT,0,4)-1||'1231';

V_FINC_STAT_CD  VARCHAR(100) :='390-001' ;


V_BACK_YY    VARCHAR(20) :=SUBSTR(P_FRM_DT,0,4)-1;

V_THIS_YY    VARCHAR(20) :=SUBSTR(P_FRM_DT,0,4);



BEGIN

....  

END SP_ACT_BS;



1



       UPDATE /*+ BYPASS_UJVC */

            (SELECT A.THIS_REM_AMT AS SET_THIS_AMT,

                    A.BACK_REM_AMT AS SET_BACK_AMT,

                    B.THIS_AMT AS GET_THIS_AMT,

                    B.BACK_AMT AS GET_BACK_AMT 

               FROM ACT_ACCOUNT A

                    INNER JOIN(SELECT C.SEQ,

                                      SUM(CASE WHEN TEMP1 = V_THIS_YY THEN DECODE(B.DR_CR,'D',TO_NUMBER(TEMP6-TEMP7),TO_NUMBER(TEMP7-TEMP6)) ELSE 0 END) AS THIS_AMT,

                                      SUM(CASE WHEN TEMP1 = V_BACK_YY THEN DECODE(B.DR_CR,'D',TO_NUMBER(TEMP6-TEMP7),TO_NUMBER(TEMP7-TEMP6)) ELSE 0 END) AS BACK_AMT

                                 FROM ACT_PRT A 

                                      INNER JOIN ACT_ACCT_CD B ON A.TEMP3 = B.ACCT_CD

                                      INNER JOIN ACT_FS_DTL  C ON B.ACCT_CD = C.ACCT_CD AND C.BUSI_PLC_CD = P_BUSI_PLC_CD AND C.FINC_STAT_CD = V_FINC_STAT_CD

                                WHERE TEMP_CLS = 'SP_ACT_BS' 

                                  AND C.SEQ = MST.SEQ

                                GROUP BY C.SEQ)B ON A.SEQ = B.SEQ

              WHERE BUSI_PLC_CD = P_BUSI_PLC_CD AND FINC_STAT_CD = V_FINC_STAT_CD AND A.SEQ = MST.SEQ

             ) SET SET_THIS_AMT = GET_THIS_AMT,

                   SET_BACK_AMT = GET_BACK_AMT;

                                                                                              ▽

             

     MERGE INTO ACT_ACCOUNT A 

     USING (

        SELECT C.SEQ,

        SUM(CASE WHEN TEMP1 = V_THIS_YY THEN DECODE(B.DR_CR,'D',TO_NUMBER(TEMP6-TEMP7),TO_NUMBER(TEMP7-TEMP6)) ELSE 0 END) AS THIS_AMT,

        SUM(CASE WHEN TEMP1 = V_BACK_YY THEN DECODE(B.DR_CR,'D',TO_NUMBER(TEMP6-TEMP7),TO_NUMBER(TEMP7-TEMP6)) ELSE 0 END) AS BACK_AMT

        FROM ACT_PRT A 

        INNER JOIN ACT_ACCT_CD B ON A.TEMP3 = B.ACCT_CD

        INNER JOIN ACT_FS_DTL  C ON B.ACCT_CD = C.ACCT_CD AND C.BUSI_PLC_CD = P_BUSI_PLC_CD AND C.FINC_STAT_CD = V_FINC_STAT_CD

        WHERE TEMP_CLS = 'SP_ACT_BS' 

        AND C.SEQ = MST.SEQ

        GROUP BY C.SEQ    

      ) B ON (A.SEQ=B.SEQ)

      WHEN MATCHED THEN 

        UPDATE 

          SET A.THIS_REM_AMT = B.THIS_AMT,

                  A.BACK_REM_AMT = B.BACK_AMT

            WHERE  BUSI_PLC_CD = P_BUSI_PLC_CD AND FINC_STAT_CD = V_FINC_STAT_CD AND A.SEQ = MST.SEQ

          ;




2.



    UPDATE /*+ BYPASS_UJVC */

         (SELECT A.THIS_REM_AMT AS SET_THIS_REM_AMT,

                 A.BACK_REM_AMT AS SET_BACK_REM_AMT,

                 A.THIS_CN_REM_AMT AS SET_THIS_CN_REM_AMT,

                 A.BACK_CN_REM_AMT AS SET_BACK_CN_REM_AMT,

                 B.THIS_REM_AMT AS GET_THIS_REM_AMT,

                 B.BACK_REM_AMT AS GET_BACK_REM_AMT

           FROM ACT_ACCOUNT A

                INNER JOIN (SELECT THIS_REM_AMT,BACK_REM_AMT

                              FROM ACT_ACCOUNT

                             WHERE BUSI_PLC_CD = P_BUSI_PLC_CD

                               AND FINC_STAT_CD = '390-002'

                               AND SEQ = '7700')B

                   ON 1=1

          WHERE A.FINC_STAT_CD = V_FINC_STAT_CD

            AND A.SEQ = '7300'

         )  SET SET_THIS_REM_AMT = GET_THIS_REM_AMT,

                SET_BACK_REM_AMT = GET_BACK_REM_AMT,

                SET_THIS_CN_REM_AMT = GET_THIS_REM_AMT,

                SET_BACK_CN_REM_AMT = GET_BACK_REM_AMT;

                                                                                                            ▽

       

    MERGE INTO ACT_ACCOUNT  A

           USING (

           SELECT THIS_REM_AMT,BACK_REM_AMT

           FROM ACT_ACCOUNT

           WHERE BUSI_PLC_CD = P_BUSI_PLC_CD

           AND FINC_STAT_CD = '390-002'

          AND SEQ = '7700'          

           ) B 

           ON(1=1)

           WHEN MATCHED THEN 

             UPDATE 

               SET A.THIS_REM_AMT = B.THIS_REM_AMT,

                A.BACK_REM_AMT = B.BACK_REM_AMT,

                A.THIS_CN_REM_AMT = B.THIS_REM_AMT,

                A.BACK_CN_REM_AMT = B.BACK_REM_AMT

                WHERE A.FINC_STAT_CD = V_FINC_STAT_CD

                AND A.SEQ = '7300';

                

                




3.

         

    UPDATE /*+ BYPASS_UJVC */

         ( SELECT A.SEQ AS SEQ,THIS_R_AMT AS SET_THIS_R_AMT,

                  BACK_R_AMT AS SET_BACK_R_AMT,

                  THIS100,

                  BACK100,

                  THIS4600,

                  BACK4600,

                  THIS6300,

                  BACK6300

             FROM ACT_ACCOUNT A

                  INNER JOIN(SELECT SEQ, 

                                    SUM(CASE  WHEN  SEQ = '100'   THEN THIS_R_AMT ELSE 0 END) OVER() AS THIS100,

                                    SUM(CASE  WHEN  SEQ = '100'   THEN BACK_R_AMT ELSE 0 END) OVER() AS BACK100,

                                    SUM(CASE  WHEN  SEQ = '4600'  THEN THIS_R_AMT ELSE 0 END) OVER() AS THIS4600,

                                    SUM(CASE  WHEN  SEQ = '4600'  THEN BACK_R_AMT ELSE 0 END) OVER() AS BACK4600,

                                    SUM(CASE  WHEN  SEQ = '6300'  THEN THIS_R_AMT ELSE 0 END) OVER() AS THIS6300,

                                    SUM(CASE  WHEN  SEQ = '6300'  THEN BACK_R_AMT ELSE 0 END) OVER() AS BACK6300

                               FROM ACT_ACCOUNT

                              WHERE BUSI_PLC_CD = P_BUSI_PLC_CD

                                AND FINC_STAT_CD = V_FINC_STAT_CD)B ON A.SEQ = B.SEQ

            WHERE BUSI_PLC_CD = P_BUSI_PLC_CD

              AND FINC_STAT_CD = V_FINC_STAT_CD

              AND A.SEQ IN('4500','6200','7400','7500')

          ) SET SET_THIS_R_AMT = CASE WHEN SEQ = '4500' THEN THIS100

                                      WHEN SEQ = '6200' THEN THIS4600 

                                      WHEN SEQ = '7400' THEN THIS6300 

                                      WHEN SEQ = '7500' THEN THIS4600+THIS6300 END,

                SET_BACK_R_AMT = CASE WHEN SEQ = '4500' THEN BACK100

                                      WHEN SEQ = '6200' THEN BACK4600 

                                      WHEN SEQ = '7400' THEN BACK6300 

                                      WHEN SEQ = '7500' THEN BACK4600+BACK6300 END;


                                                                                                            ▽



        MERGE INTO ACT_ACCOUNT A

        USING (SELECT SEQ, 

        SUM(CASE  WHEN  SEQ = '100'   THEN THIS_R_AMT ELSE 0 END) OVER() AS THIS100,

        SUM(CASE  WHEN  SEQ = '100'   THEN BACK_R_AMT ELSE 0 END) OVER() AS BACK100,

        SUM(CASE  WHEN  SEQ = '4600'  THEN THIS_R_AMT ELSE 0 END) OVER() AS THIS4600,

        SUM(CASE  WHEN  SEQ = '4600'  THEN BACK_R_AMT ELSE 0 END) OVER() AS BACK4600,

        SUM(CASE  WHEN  SEQ = '6300'  THEN THIS_R_AMT ELSE 0 END) OVER() AS THIS6300,

        SUM(CASE  WHEN  SEQ = '6300'  THEN BACK_R_AMT ELSE 0 END) OVER() AS BACK6300

         FROM ACT_ACCOUNT

         WHERE BUSI_PLC_CD = P_BUSI_PLC_CD

         AND FINC_STAT_CD = V_FINC_STAT_CD

        ) B

        ON (A.SEQ = B.SEQ)

       WHEN MATCHED THEN

        UPDATE 

        SET  A.THIS_R_AMT = CASE WHEN SEQ = '4500' THEN THIS100

                                      WHEN SEQ = '6200' THEN THIS4600 

                                      WHEN SEQ = '7400' THEN THIS6300 

                                      WHEN SEQ = '7500' THEN THIS4600+THIS6300 END,

                A.BACK_R_AMT = CASE WHEN SEQ = '4500' THEN BACK100

                                      WHEN SEQ = '6200' THEN BACK4600 

                                      WHEN SEQ = '7400' THEN BACK6300 

                                      WHEN SEQ = '7500' THEN BACK4600+BACK6300 END

        WHERE BUSI_PLC_CD = P_BUSI_PLC_CD

              AND FINC_STAT_CD = V_FINC_STAT_CD

              AND A.SEQ IN('4500','6200','7400','7500');



머지문을 추가로 공부 하자. !!  

Listing 4. Merge delete

MERGE INTO customer as C
USING customer_trans as CT
ON C.customer_num != CT.customer_num
WHEN MATCHED THEN  DELETE
WHEN NOT MATCHED THEN INSERT VALUES (CT.customer_num, CT.fname, 
CT.lname, CT.company, CT.address1, CT.address2, CT.city, 
CT.state, CT.zipcode, CT.phone);

Listing 5 shows other usages of the merge statement where the WHEN MATCHED and WHEN NOT MATCHED clauses are optionally used. When one of these clauses is not specified, that part of the merge is ignored. In the first merge statement, all the non-matching rows would be ignored and matched rows would be updated in the sale table. Similarly, in the second mergestatement, only non-matched rows would be inserted into the customer table, and all the matched rows would be ignored.


Listing 5. Exclusive update, insert, or delete
MERGE INTO customer AS C
USING customer_trans AS CT
ON C.customer_num = CT.customer_num
WHEN MATCHED THEN UPDATE SET C.phone = CT.phone;

MERGE INTO customer as C
USING customer_trans as CT
ON C.customer_num = CT.customer_num
WHEN NOT MATCHED THEN INSERT VALUES (CT.customer_num, CT.fname, 
CT.lname, CT.company, CT.address1, CT.address2, CT.city, CT.state,
CT.zipcode, CT.phone);

The Informix implementations of the merge statement allow the flexibility of ordering the WHEN MATCHED and WHEN NOT MATCHED clauses, as shown in Listing 6.


Listing 6. Flexible ordering of update, insert, and delete clauses
MERGE INTO customer as C
USING customer_trans as CT
ON C.customer_num = CT.customer_num
WHEN NOT MATCHED THEN INSERT VALUES (CT.customer_num, CT.fname, 
CT.lname, CT.company, CT.address1, CT.address2, CT.city, CT.state,
CT.zipcode, CT.phone)
WHEN MATCHED THEN UPDATE SET C.phone = CT.phone;

하나의 쿼리에서 INSERT DELETE UPDATE 된다. 


MERGE 문 성능 최적화

SQL Server 2008 R2
이 항목은 아직 평가되지 않았습니다.이 항목 평가

SQL Server 2008에서는 MERGE 문을 사용하여 단일 문에서 여러 DML(데이터 조작 언어) 작업을 수행할 수 있습니다. 예를 들어 원본 테이블과의 차이점에 따라 대상 테이블에서 행을 삽입, 업데이트 및 삭제하여 두 테이블을 동기화해야 하는 경우가 있습니다. 일반적으로 이러한 작업은 개별 INSERT, UPDATE 및 DELETE 문을 포함하는 저장 프로시저 또는 일괄 처리를 실행하여 수행합니다. 그러나 이는 원본 테이블과 대상 테이블의 데이터가 적어도 각 문에 대해 한 번씩 여러 번 계산되고 처리됨을 의미합니다.

MERGE 문을 사용하면 여러 개의 개별 DML 문을 단일 문으로 대체할 수 있습니다. 이렇게 하면 작업이 하나의 문 내에서 수행되므로 원본 및 대상 테이블의 데이터가 처리되는 횟수가 최소화되어 쿼리 성능이 향상됩니다. 단, 성능 향상을 위해서는 인덱스, 조인 및 기타 고려 사항이 올바르게 설정되어야 합니다. 이 항목에서는 MERGE 문을 사용할 때 최적의 성능을 얻는 데 도움이 되는 최선의 방법 권장 사항에 대해 설명합니다.

MERGE 문의 성능을 높이려면 다음 인덱스 지침을 따르는 것이 좋습니다.

  • 원본 테이블의 조인 열에 고유한 포함 인덱스를 만듭니다.

  • 대상 테이블의 조인 열에 고유한 클러스터형 인덱스를 만듭니다.

이러한 인덱스를 통해 조인 키의 고유성을 확보하고 테이블의 데이터가 정렬되도록 할 수 있습니다. 쿼리 최적화 프로그램이 중복 행을 찾아 업데이트하기 위해 별도로 유효성 검사를 수행할 필요가 없고 추가 정렬 작업도 필요 없으므로 쿼리 성능이 개선됩니다.

예를 들어 다음 MERGE 문에서 원본 테이블 dbo.Purchases와 대상 테이블 dbo.FactBuyingHabits는 ProductID 및 CustomerID 열에서 조인됩니다. 이 문의 성능을 높이려면 dbo.Purchases 테이블의 ProductID 및CustomerID 열에 고유 또는 기본 키 인덱스(클러스터형 또는 비클러스터형)를 만들고 dbo.FactBuyingHabits 테이블의 ProductID 및 CustomerID 열에 클러스터형 인덱스를 만듭니다. 이 테이블을 만드는 데 사용된 코드는MERGE를 사용하여 데이터 삽입, 업데이트 및 삭제에서 볼 수 있습니다.

MERGE dbo.FactBuyingHabits AS Target
USING (SELECT CustomerID, ProductID, PurchaseDate FROM dbo.Purchases) AS Source
ON (Target.ProductID = Source.ProductID AND Target.CustomerID = Source.CustomerID)
WHEN MATCHED THEN
    UPDATE SET Target.LastPurchaseDate = Source.PurchaseDate
WHEN NOT MATCHED BY TARGET THEN
    INSERT (CustomerID, ProductID, LastPurchaseDate)
    VALUES (Source.CustomerID, Source.ProductID, Source.PurchaseDate)
OUTPUT $action, Inserted.*, Deleted.*; 


MERGE 문의 성능을 높이고 정확한 결과를 얻으려면 다음 조인 지침을 따르는 것이 좋습니다.

  • ON <merge_search_condition> 절에는 원본 및 대상 테이블의 데이터 비교를 위한 조건을 나타내는 검색 조건만 지정합니다. 즉, 대상 테이블에서 원본 테이블의 해당 열과 비교할 열만 지정해야 합니다. 상수와 같은 다른 값에 대한 비교는 포함하지 않습니다.

원본 또는 대상 테이블에서 행을 필터링하려면 다음 방법 중 하나를 사용합니다.

  • 적절한 WHEN 절에 행 필터링을 위한 검색 조건을 지정합니다(예: WHEN NOT MATCHED AND S.EmployeeName LIKE 'S%' THEN INSERT...).

  • 필터링된 행을 반환하는 원본 또는 대상에 대한 뷰를 정의하고 이 뷰를 원본 또는 대상 테이블로 참조합니다. 대상 테이블에 대해 정의된 뷰의 모든 동작은 뷰 업데이트를 위한 조건을 충족해야 합니다. 뷰를 사용하여 데이터를 업데이트하는 방법은 뷰를 통해 데이터 수정을 참조하십시오.

  • WITH <common table expression> 절을 사용하여 원본 또는 대상 테이블에서 행을 필터링합니다. 이 방법은 ON 절에 추가 검색 조건을 지정하는 것과 비슷하며 잘못된 결과를 생성할 수 있으므로 사용하지 않는 것이 좋으며 사용할 경우 구현 전에 철저히 테스트해야 합니다.

자세한 내용은 MERGE를 사용하여 데이터 삽입, 업데이트 및 삭제를 참조하십시오.

조인의 쿼리 최적화

MERGE 문의 조인 작업은 SELECT 문의 조인과 동일한 방식으로 최적화됩니다. 즉, SQL Server에서 조인을 처리할 때 쿼리 최적화 프로그램은 여러 가지 가능한 방법 중 가장 효율적인 방법을 선택합니다. 조인에 대한 자세한 내용은 조인 기본 사항 및 고급 쿼리 튜닝 개념을 참조하십시오. 원본 및 대상의 크기가 비슷하고 ‘인덱스를 위한 최선의 방법’ 섹션에 설명된 인덱스 지침을 원본 및 대상 테이블에 적용한 경우 merge join 연산자가 가장 효율적인 쿼리 계획입니다. 두 테이블 모두 한 번만 검색되고 데이터를 정렬할 필요가 없기 때문입니다. 원본 테이블이 대상 테이블보다 작은 경우 nested loops 연산자가 좋습니다.

MERGE 문에서 OPTION (<query_hint>) 절을 지정하여 특정 조인을 사용하도록 강제할 수 있습니다. 해시 조인은 인덱스를 사용하지 않으므로 MERGE 문에 대한 쿼리 힌트로는 사용하지 않는 것이 좋습니다. 쿼리 힌트에 대한 자세한 내용은 쿼리 힌트(Transact-SQL)를 참조하십시오. 다음 예에서는 OPTION 절에 중첩 루프 조인을 지정합니다.

USE AdventureWorks2008R2;
GO
BEGIN TRAN;
MERGE Production.ProductInventory AS pi
USING (SELECT ProductID, SUM(OrderQty) 
       FROM Sales.SalesOrderDetail AS sod
       JOIN Sales.SalesOrderHeader AS soh
         ON sod.SalesOrderID = soh.SalesOrderID
         AND soh.OrderDate BETWEEN '20030701' AND '20030731'
       GROUP BY ProductID) AS src(ProductID, OrderQty)
ON (pi.ProductID = src.ProductID)
WHEN MATCHED AND pi.Quantity - src.OrderQty >= 0 
    THEN UPDATE SET pi.Quantity = pi.Quantity - src.OrderQty
WHEN MATCHED AND pi.Quantity - src.OrderQty <= 0 
    THEN DELETE
OUTPUT $action, Inserted.*, Deleted.*
OPTION (LOOP JOIN);
GO
ROLLBACK TRAN;


SELECT, INSERT, UPDATE 또는 DELETE 문이 매개 변수 없이 실행되는 경우 SQL Server 쿼리 최적화 프로그램은 내부적으로 문을 매개 변수화하는 방법을 선택할 수 있습니다. 즉, 쿼리에 포함된 모든 리터럴 값이 매개 변수로 대체됩니다. 예를 들어 INSERT dbo.MyTable (Col1, Col2) VALUES (1, 10) 문은 내부적으로 INSERT dbo.MyTable (Col1, Col2) VALUES (@p1, @p2)로 구현될 수 있습니다. 단순 매개 변수화라고 하는 이 프로세스는 새 SQL 문을 이전에 컴파일된 기존의 실행 계획과 비교하는 관계형 엔진의 기능을 개선합니다. 쿼리 컴파일 및 다시 컴파일 빈도가 낮아지므로 쿼리 성능이 개선될 수 있습니다. 쿼리 최적화 프로그램은 MERGE 문에 단순 매개 변수화 프로세스를 적용하지 않습니다. 따라서 리터럴 값을 포함하는 MERGE 문의 경우 MERGE 문이 실행될 때마다 새 계획이 컴파일되므로 개별 INSERT, UPDATE 또는 DELETE 문보다 성능이 떨어집니다.

쿼리 성능을 높이려면 다음 매개 변수화 지침을 따르는 것이 좋습니다.

  • ON <merge_search_condition> 절과 MERGE 문의 WHEN 절에 있는 모든 리터럴 값을 매개 변수화합니다. 예를 들어 리터럴 값을 적절한 입력 매개 변수로 대체하여 MERGE 문을 저장 프로시저에 통합할 수 있습니다.

  • 문을 매개 변수화할 수 없는 경우 TEMPLATE 형식의 계획 지침을 만들고 PARAMETERIZATION FORCED 쿼리 힌트를 이 계획 지침에 지정합니다. 자세한 내용은 계획 지침을 사용하여 쿼리 매개 변수화 동작 지정을 참조하십시오.

  • MERGE 문이 데이터베이스에서 자주 실행되는 경우 데이터베이스의 PARAMETERIZATION 옵션을 FORCED로 설정하는 것이 좋습니다. 이 옵션을 설정할 때는 신중해야 합니다. PARAMETERIZATION 옵션은 데이터베이스 수준 설정이므로 데이터베이스에 대한 모든 쿼리의 처리 방식에 영향을 미칩니다. 자세한 내용은 강제 매개 변수화를 참조하십시오.

MERGE 문에서 TOP 절은 원본 테이블과 대상 테이블이 조인되고 삽입, 업데이트 또는 삭제 동작에 적합하지 않은 행이 제거된 후에 영향을 받는 행의 개수나 비율을 지정합니다. TOP 절은 조인된 행 수를 지정된 값으로 더 줄이며, 삽입, 업데이트 또는 삭제 동작은 나머지 조인된 행에 순서 없이 적용됩니다. 즉, 행은 WHEN 절에 정의된 동작에 순서 없이 분산됩니다. 예를 들어 TOP (10)을 지정하면 10개 행이 영향을 받습니다. 이 10개의 행 중 7개가 업데이트되고 3개가 삽입되거나, 1개가 삭제되고 5개가 업데이트되고 4개가 삽입될 수 있습니다.

일반적으로 TOP 절을 사용하여 큰 테이블에서 일괄 처리로 DML(데이터 조작 언어) 작업을 수행합니다. MERGE 문에서 TOP 절을 이러한 용도로 사용하는 경우 다음 내용을 알고 있어야 합니다.

  • I/O 성능이 영향을 받을 수 있습니다.

    MERGE 문은 원본 테이블과 대상 테이블 모두에서 전체 테이블 검색을 수행합니다. 작업을 여러 일괄 처리로 나누면 일괄 처리 하나당 수행되는 쓰기 작업의 수를 줄일 수 있지만 각 일괄 처리에서는 원본 테이블과 대상 테이블에서 전체 테이블 검색을 수행합니다. 그 결과 읽기 작업이 쿼리의 성능에 영향을 줄 수 있습니다.

  • 잘못된 결과가 발생할 수 있습니다.

    연속된 모든 일괄 처리는 새로운 행을 대상으로 해야 합니다. 그렇지 않은 경우 대상 테이블에 중복 행을 잘못 삽입하는 등의 원하지 않는 동작이 발생할 수 있습니다. 이러한 현상은 대상 일괄 처리에는 없었지만 전체 대상 테이블에는 있었던 행이 원본 테이블에 포함되는 경우 발생할 수 있습니다.

    정확한 결과를 보장하려면

    • ON 절을 사용하여 기존 대상 행에 영향을 미치는 원본 행과 완전히 새로운 행을 확인합니다.

    • WHEN MATCHED 절에 추가 조건을 사용하여 이전 일괄 처리에 의해 대상 행이 이미 업데이트되었는지 여부를 확인합니다.

    TOP 절은 이러한 절이 적용된 후에 적용되므로 문을 실행할 때마다 하나의 완전히 일치하지 않는 행이 삽입되거나 하나의 기존 행이 업데이트됩니다. 다음 예에서는 원본 및 대상 테이블을 만든 다음 일괄 처리 작업에서 올바르게 TOP 절을 사용하여 대상을 수정하는 방법을 알려 줍니다.

    CREATE TABLE dbo.inventory(item_key int NOT NULL PRIMARY KEY, amount int, is_current bit);
    GO
    CREATE TABLE dbo.net_changes(item_key int NOT NULL PRIMARY KEY, amount int);
    GO
    
    MERGE TOP(1) dbo.inventory
    USING dbo.net_changes
    ON inventory.item_key = net_changes.item_key
    WHEN MATCHED AND inventory.is_current = 0
      THEN UPDATE SET amount += net_changes.amount, is_current = 1
    WHEN NOT MATCHED BY TARGET
      THEN INSERT (item_key, amount, is_current) VALUES(item_key, amount, 1)
    OUTPUT deleted.*, $action, inserted.*;
    GO
    
    
    

    다음 예에서는 잘못된 TOP 절 구현 방법을 보여 줍니다. is_current 열 확인이 원본 테이블과의 조인 조건에 지정됩니다. 즉, 하나의 일괄 처리에 사용된 원본 행이 다음 일괄 처리에 “일치하지 않는 항목”으로 처리되어 원하지 않는 삽입 작업이 수행될 수 있습니다.

    MERGE TOP(1) dbo.inventory
    USING dbo.net_changes
    ON inventory.item_key = net_changes.item_key AND inventory.is_current = 0
    WHEN MATCHED
      THEN UPDATE SET amount += net_changes.amount, is_current = 1
    WHEN NOT MATCHED BY TARGET
      THEN INSERT (item_key, amount, is_current) values(item_key, amount, 1)
    OUTPUT deleted.*, $action, inserted.*;
    GO
    
    
    

    다음 예에서도 잘못된 방법을 보여 줍니다. CTE(공통 테이블 식)를 사용하여 일괄 처리의 행 읽기 횟수를 제한하면 TOP(1)에 의해 선택된 행이 아닌 다른 대상 행에 일치하는 모든 원본 행이 “일치하지 않는 항목”으로 처리되어 원하지 않는 삽입 작업이 수행될 수 있습니다. 또한 이 방법은 업데이트할 수 있는 행의 수만 제한하므로 각 일괄 처리에서는 “일치하지 않는” 모든 원본 행의 삽입을 시도합니다.

    WITH target_batch AS (
      SELECT TOP(1) *
      FROM dbo.inventory
      WHERE is_current = 0
      )
    MERGE target_batch
    USING dbo.net_changes
    ON target_batch.item_key = net_changes.item_key
    WHEN MATCHED
      THEN UPDATE SET amount += net_changes.amount, is_current = 1
    WHEN NOT MATCHED BY TARGET
      THEN INSERT (item_key, amount, is_current) values(item_key, amount, 1)
    OUTPUT deleted.*, $action, inserted.*;
    GO
    
    
    

MERGE 문을 사용하여 OPENROWSET(BULK…) 절을 테이블 원본으로 지정하면 원본 데이터 파일의 데이터를 대상 테이블로 효율적으로 대량 로드할 수 있습니다. 이렇게 하면 전체 파일이 하나의 일괄 처리에서 처리됩니다.

대량 병합 프로세스의 성능을 높이려면 다음 지침을 따르는 것이 좋습니다.

  • 대상 테이블의 조인 열에 클러스터형 인덱스를 만듭니다.

  • OPENROWSET(BULK…) 절에 ORDER 및 UNIQUE 힌트를 사용하여 원본 데이터 파일의 정렬 방식을 지정합니다.

    기본적으로 대량 작업은 데이터 파일이 정렬되지 않았음을 전제로 합니다. 따라서 쿼리 최적화 프로그램이 보다 효율적인 쿼리 계획을 생성할 수 있도록 원본 데이터를 대상 테이블의 클러스터형 인덱스에 따라 정렬하고, ORDER 힌트를 사용하여 순서를 나타내야 합니다. 힌트는 런타임에 유효성이 검사됩니다. 데이터 스트림이 지정된 힌트를 따르지 않으면 오류가 발생합니다.

이러한 지침을 통해 조인 키의 고유성을 확보하고 원본 파일의 데이터 정렬 순서가 대상 테이블과 일치하도록 할 수 있습니다. 추가 정렬 작업이 필요 없고 불필요한 데이터 복사가 없으므로 쿼리 성능이 향상됩니다. 다음 예에서는 MERGE 문을 사용하여 플랫 파일 StockData.txt에서 대상 테이블 dbo.Stock으로 데이터를 대량 로드합니다. 대상 테이블의 StockName에 기본 키 제약 조건을 정의하면 원본 데이터와 조인하는 데 사용되는 열에 클러스터형 인덱스가 만들어집니다. ORDER 및 UNIQUE 힌트는 대상 테이블의 클러스터형 인덱스 키 열에 매핑되는 데이터 원본의 Stock 열에 적용됩니다.

이 예를 실행하려면 먼저 C:\SQLFiles\ 폴더에 'StockData.txt'라는 텍스트 파일을 만듭니다. 이 파일에는 쉼표로 구분된 두 개의 데이터 열이 있어야 합니다. 예를 들어 다음과 같은 데이터를 사용합니다.

Alpine mountain bike,100

Brake set,22

Cushion,5

그런 다음 C:\SQLFiles\ 폴더에 'BulkloadFormatFile.xml'이라는 xml 서식 파일을 만듭니다. 다음과 같은 정보를 사용합니다.

<?xml version="1.0"?>

<BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<RECORD>

<FIELD ID="1" xsi:type="CharTerm" TERMINATOR="," MAX_LENGTH="25"/>

<FIELD ID="2" xsi:type="CharTerm" TERMINATOR="\r\n" MAX_LENGTH="5"/>

</RECORD>

<ROW>

<COLUMN SOURCE="1" NAME="Stock" xsi:type="SQLNVARCHAR"/>

<COLUMN SOURCE="2" NAME="Delta" xsi:type="SQLSMALLINT"/>

</ROW>

</BCPFORMAT>

USE AdventureWorks2008R2;
GO
CREATE TABLE dbo.Stock (StockName nvarchar(50) PRIMARY KEY, Qty int CHECK (Qty > 0));
GO
MERGE dbo.Stock AS s
USING OPENROWSET (
    BULK 'C:\SQLFiles\StockData.txt',
    FORMATFILE = 'C:\SQLFiles\BulkloadFormatFile.xml',
    ROWS_PER_BATCH = 15000,
    ORDER (Stock) UNIQUE) AS b
ON s.StockName = b.Stock
WHEN MATCHED AND (Qty + Delta = 0) THEN DELETE
WHEN MATCHED THEN UPDATE SET Qty += Delta
WHEN NOT MATCHED THEN INSERT VALUES (Stock, Delta);
GO


다음 기능을 사용하여 MERGE 문의 성능을 측정 및 진단할 수 있습니다.

  • sys.dm_exec_query_optimizer_info 동적 관리의 merge stmt 카운터를 사용하여 MERGE 문에 대한 쿼리 최적화 횟수를 반환할 수 있습니다.

  • sys.dm_exec_plan_attributes 동적 관리 함수의 merge_action_type 특성을 사용하여 MERGE 문의 결과로 사용되는 트리거 실행 계획의 유형을 반환할 수 있습니다.

  • SQL Trace를 사용하여 다른 DML(데이터 조작 언어) 문에 대해 사용하는 것과 동일한 방식으로 MERGE 문에 대한 문제 해결 데이터를 수집할 수 있습니다. 자세한 내용은 SQL Trace 소개를 참조하십시오.

이 정보가 도움이 되었습니까?  


'오라클' 카테고리의 다른 글

프로시져.INSERT 또는 UPDATE  (0) 2012.10.15
오라클 힌트 모음 (예제)  (0) 2012.08.31
오라클 procedure 정리  (0) 2012.08.30
오라클 FUNCTION() 생성 예제  (0) 2012.08.30
DB2 함수 정리.  (0) 2012.08.29
Posted by 사라링
BLOG main image
.. by 사라링

카테고리

사라링님의 노트 (301)
JSP (31)
J-Query (41)
JAVA (24)
디자인패턴 (1)
스트러츠 (3)
안드로이드 (11)
오라클 (45)
우분투-오라클 (1)
이클립스메뉴얼 (6)
스프링3.0 (23)
자바스크립트 (10)
HTML5.0 (17)
정보처리기사 (1)
기타(컴퓨터 관련) (1)
문제점 해결 (3)
프로젝트 (2)
AJAX (4)
하이버네이트 (3)
트러스트폼 (11)
Jeus (2)
재무관리(회계) (5)
정규식 (5)
아이바티스 (8)
취미 (2)
소프트웨어 보안 관련모음 (0)
정보보안기사 (6)
C언어 베이직 및 프로그램 (3)
보안 관련 용어 정리 (2)
넥사크로 (6)
웹스퀘어_ (0)
Total :
Today : Yesterday :