/
바이 패스 문을 머지 문으로 변경 하자.
*목표*
프로시져 에서 바이패스 문으로 작성된 UPDATE 문 3개를 머지문을 이용 하여 사용 할수 있도록 하라.
업데이트 문이 기본적으로 수정을 목표로 한다면 머지 문은 INSERT DELETE UPDATE 를 하나의 쿼리 내에서 실행 할수 잇는 개념으로 보면 될듯 하다.
왜그래야하징?
where col='a'
set a.d=b.d
여기서 (select ~) 안에는 join 등을 이용 하여 여러개의 테이블을 검색 하여 검색된 컬럼 내에서 의 값들을 검색된 컬럼으로 setting 한다.
BYPASS_UJVC 라는 힌트를 사용하여 튜닝할 수 있다.
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');
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 merge
statement, 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에서는 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 |