MERGE
UPDATE,INSERT,DELETE 의 MULTIPLE OPERATION 0N 을 지원하기 위한 DML 구문으로,Source Table 에서 추출한 데이터를 Target Table 에 트랜잭션을 처리하는 형태 입니다.
1. UPSERT (UPDATE , INSERT)
UPSERT 구문이란 UPDATE 와 INSERT 를 처리하는 프로그램 로직을 처리 가능하도록 구현한 것이다.
2. Only UPDATE (UPDATABLE) JOIN VIEW 대체
UPDATE 시 SET 절에 서브쿼리가존재해 UPDATE 건수만큼 반복 수행하여 성능 문제가 발생할 경우, 성능개선 방법 JOIN VIEW을 사용
UPSERT 와 Only UPDATE 의 차이점
1. MERGE 구문은 병렬처리가 가능할 뿐 아니라 UPDATABLE JOIN VIEW 보다 성능적인 측면에서 효율적이다.
2. MERGE 구문 작성시 제약사항이 많지 않아 쉽게 작성할 수 있는 장점
<1.쿼리>
UPDATE emp a
SET ename = (SELECT dname FROM dept b WHERE a.deptno = b.deptno)
WHERE a.empno > 0 ;
<2.쿼리>
UPDATE /*+ BYPASS UJVC */
(
SELECT b.dname , a.ename , a.deptn。
FROM emp a , dept b
WHERE a.deptno = b.deptno(+)
AND a.empno > 0
)
SET ename = dname ;
<3.쿼리>
ALTER SESSION ENABLE PARALLEL DML;
MERGE /*+ PARALLEL(A2) USE_HASH(B) */ INTO emp a
USING (
SELECT / *+ FULL© PARALLEL(C2) */
c .dname , c.deptn。
FROM dept c
) b
ON (
a . deptno = b.deptno(+) and a . empno > 0
WHEN MATCHED THEN
UPDATE SET a . ename = b . dname ;
MERGE 구문을 이해하기 위해 구문의 구성요소에 대해 설명에 필요한 테이블을 생성
< MERGE T1 >
. 테이블 생성
create table merge_t1
as
select level as c1 , chr(65+mod(level, 26)) as c2 , level+99999 as c3
from DUAL
connect by level <= 100000 ;
. 각 컬럼에 인텍스 생성 및 통계정보 수집
create index merge_t1_idx_01 on merge_t1 ( c1 ) ;
exec
dbms_stats.gather_table_stats(ownname=> 'SCOTT' ,tabname=> 'merge_t1', cascade=>TRUE ,
estimate percent=>100) ;
< MERGE T2 >
. 테이블 생성
create table merge_t2
as
select level as c1 , chr (65+mod(level, 26)) as c2,
decode(mod(level, 10) , 0, null , mod(level , 10)) as c3
from DUAL
connect by level <= 500000 ;
!! 각 컬럼에 인텍 스 생성 및 통계정보 수집
create index merge_t2_idx_01 on merge_t2 ( cl ) ;
exec
dbms_stats.gather_table_stats(ownname => 'SCOTT' , tabname=> 'merge_t2' ,cascade=>TRUE ,
estimate_percent=>100)
MERGE 구문의 구성요소 알기
INTO절
INTO 절은 크게 두 가지 역할을 담당한다.
1. Target Table 을 정의 할 수 있다.
Target Table 이란 UPDATE , DELETE , INSERT을 수행할 대상 테이블 1개를 정의
2. 힌트 구문을 적용할 수 있다는 것이다.
힌트 구문은 조인 순서힌트, 조인 방식힌트, 테이블 엑세스 유형 힌트 등 대부분의 힌트를 적용 할 수 있다.
HERGE /*+ LEADING(ST) USE_NL(ST TT) INDEX(TT) */ INTO MERGE_T1 tt
USING (
SELECT c1 , c2 , c3
FROM MERGE_T2
WHERE c1 >= 99990
AND c1 <= 100090
) st
ON ( tt.c1 = st.c1 )
WHEN MATCHED THEN
UPDATE SET tt.c2 = st.c2 , tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A')
WHEN NOT MATCHED THEN
INSERT ( tt.c1, tt.c2 , tt.c3) VALUES (st.c1 , st.c2, st.c3)
WHERE (st.c2 = 'A' ) ;
MERGE 구문에서는 DML 까지 병렬처리를 수행할 수 있는데, PARALLEL DML 을 적용하기 위해서는 세션에서 PARALLEL DML 이 지원되어야 병렬처리가 가능하다.
ALTER SESSION ENABLE PARALLEL DML; ---> PARALLEL DML 을 수행할 수 있도록 설정
HERGE /*+ LEADING(ST) USE_NL(ST TT) INDEX(TT) PARALLEL(TT 4)*/ INTO MERGE_T1 tt
USING (
SELECT c1 , c2 , c3
FROM MERGE_T2
WHERE c1 >= 99990
AND c1 <= 100090
) st
ON ( tt.cl = st.cl )
WHEN MATCHED THEN
UPDATE SET tt.c2 = st.c2 , tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A')
WHEN NOT MATCHED THEN
INSERT ( tt.c1, tt.c2 , tt.c3) VALUES (st.c1 , st.c2, st.c3)
WHERE (st.c2 = 'A' ) ;
USING절
Source Table 을 지정하고,Target Table에 UPDATE , INSERT 를 수행할 대상 데이터를 추출하며, USING 절에서도 INTO 절과 마찬가지로 힌트 구문을 적용할 수 있다.
주의할 점 : 추출 데이터 컬럼 중 ON 절에서 Target Table과 조인 할 컬럼의 값은 반드시 Unique 해야 한다.
ON절
ON 절은 Target Table의 데이터 중 Source Table에서 추줄된 데이터와 일치하는 데이터인지 체크하여 일치(WHEN MATCHED THEN) 과 불일치(WHEN NOT MATCHED THEN) 구분하여 수행한다.
1. ON (Target_Table.Column = Source_Table.Column) -> JOIN Condition
2. WHEN MATCHED THEN -> UPDATE or UPDATE & DELETE
3. WHEN NOT MATCHED THEN -> INSERT
MERGE INTO MERGE_T1 tt
USING (
SELECT c1 , c2 , c3
FROM MERGE_T2
WHERE c1 >= 99990
AND c1 <= 100090
) st
ON ( tt.c1 = st.c1 )
WHEN MATCHED THEN
UPDATE SET tt.c2 = st.c2, tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A')
WHEN NOT MATCHED THEN
INSERT (tt.c1 , tt. c2, tt.c3) VALUES (st.c1 , st.c2, st.c3)
WHERE (st.c2 = 'A')
COMMIT ; -- Merge 구문은 트랜잭션을 반영하기 위해서는 COMMIT 을 수행해야 한다.
1. MERGE 수행 전
Source Table(MERGE T2)추출된 데이터 중 Target Table(MERGE_T1)에 존재하는 데이터를 비교
COUNT(*)
COUNT(*)
COUNT(*)
2. MERGE 수행 후
MERGE 수행전
WHEN MATCHED THEN ON절의 tt.c1=st.c1 조건으로 추출된 데이터는 총 11건이고, c1 컬럼의 값은 99990 ~ 100000
WHEN NOT MATCHED THEN ON절의 tt.c1=st.c1 조건의 대상건이 아닌 데이터가 MERGE_T2에 있을시 INSERT 3건
WHEN MATCHED THEN
페이지 127(110) 그림 넣기
WHEN NOT MATCHED THEN
페이지 130(113) 그림 넣기
3. MERGE 구문 작성 시 발생할 수 있는 에러와 해결방법 알아보기
MERGE 구문 작성할 떄 발생하는 에러는 컬럼의 성격에 의해 발생하는 경우가 많다.
MERGE_T1 과 MERGE_T2의 컬럼 정보를 살펴 보자
TARGET TABLE과 SOURCE TABLE의 조건은 1:1이어야 한다.
ON절에 조인 조건으로 사용할 컬럼은 UNIQUE한 값이어야 피할 수 있다.
아래의 에러 발생 예를 통하여 내용을 확인하고 해결방법을 찾아보자
에러 발생 예[1].중복 테이터 생성으로 ON절의 MERGE_T1, MERGE_T2 조인 처리가 1:N인 경우
MERGE INTO MERGE_T1 tt
USING (
SELECT c1, c2, c3
FROM MERGE_T2 , (SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 2)
WHERE c1 >= 99990
AND c1 <= 100090
) st
ON ( tt.c1 = st.c1 )
WHEN MATCHED THEN
UPDATE SET tt.c2 = st.c2, tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A' )
WHEN NOT MATCHED THEN
INSERT (tt.c1 , tt.c2 , tt.c3) VALUES (st.c1, st.c2 , st.c3)
WHERE (st.c2 = 'A' );
ERROR at line 1:
ORA-30926: unable to get a stable set of rows in the source tables
처리 : Source Table에서 데이터 추출시 DISTINCT 나 GROUP BY처리 필요
!! DISTINCT를 추가하여 해결
MERGE INTO MERGE_T1 tt
USING (
SELECT DISTINCT c1, c2, c3
FROM MERGE_T2 , (SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 2)
WHERE c1 >= 99990
AND c1 <= 100090
) st
ON ( tt.c1 = st.c1 )
WHEN MATCHED THEN
UPDATE SET tt.c2 = st.c2, tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A' )
WHEN NOT MATCHED THEN
INSERT (tt.c1 , tt.c2 , tt.c3) VALUES (st.c1, st.c2 , st.c3)
WHERE (st.c2 = 'A' );
에러 발생 예[2].ON 절의 MERGE_Tl, MERGE_T2 조인 처리가 N:N 인 경우
MERGE INTO MERGE_T1 tt
USING (
SELECT c1, c2, c3
FROM MERGE_T2
WHERE c1 >= 99990
AND c1 <= 100090
) st
ON ( tt.c2 = st.c2 )
WHEN MATCHED THEN
UPDATE SET tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A' )
WHEN NOT MATCHED THEN
INSERT (tt.c1 , tt.c2 , tt.c3) VALUES (st.c1, st.c2 , st.c3)
WHERE (st.c2 = 'A' );
ERROR at line 1:
ORA- 30926: unable to get a stab1e set of rows in the source tables
처리 : ON절의 조인연결 컬럼 확인 후 변경
MERGE INTO MERGE_Tl tt
USING (
SELECT c1, c2, c3
FROM MERGE_T2
WHERE c1 >= 99990
AND c1 <= 100090
) st
ON ( tt.c1 = st.c1 )
WHEN MATCHED THEN
UPDATE SET tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A' )
WHEN NOT MATCHED THEN
INSERT (tt.c1 , tt.c2 , tt.c3) VALUES (st.c1, st.c2 , st.c3)
WHERE (st.c2 = 'A' );
UPDATE 컬럼은 ON절에 사용할 수 없다.
MERGE 구문을 작성하다 보면,ON절 (조인 연결)에서 사용한 컬럼을 UPDATE 까지 수행해야 할 경우가 있다.
ON절에 사용한 컬럼이 Primary Key컬럼 (또는, Unique 데이터를 가진 컬럼)이라면, 에러 없이 수행하기 위해서 Oracle 이 제공하는 ROWID를 이용한다.
ON절 사용한 컬럼을 UPDATE시 에러 발생
MERGE INTO MERGE_T1 tt
USING (
SELECT c1, c2 , c3
FROM MERGE_T2
WHERE c1 >= 99990
AND c1 <= 100090
) st
ON ( tt.c1 = st.c1 )
WHEN MATCHED THEN
UPDATE SET tt.c1 = st.c1, tt.c2 = st.c2, tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A' )
WHEN NOT MATCHED THEN
INSERT (tt.c1 , tt.c2 , tt.c3) VALUES (st.c1 , st.c2 , st.c3)
WHERE (st.c2 = 'A' );
ERROR at line 9:
ORA-38104 : Columns referenced in the ON 절 cannot be updated : "TT"."C1"
처리 : ON절에 C1컬럼 대신 ROWID를 추출하여 C1컬럼을 대체하여 수행
MERGE INTO MERGE_T1 tt
USING (
SELECT st.c1 , st.c2 , st.c3 , tt.ROWID as rid
FROM MERGE_T2 st, MERGE_Tl tt
WHERE st.c1 >= 99990
AND st.c1 <= 100090
AND st.c1 = tt . c1(+)
) st
ON ( tt.ROWID = st.rid )
WHEN MATCHED THEN
UPDATE SET tt.c1 = st.c1 , tt.c2 = st.c2, tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A')
WHEN NOT MATCHED THEN
INSERT (tt.c1 , tt.c2 , tt.c3 ) VALUES (st.c1 , st.c2 , st.c3 )
WHERE (st.c2 = 'A');
4. MERGE 구문은 다양한 형태의 DML을 지원한다
MERGE 구문은 10g 이후부터 제약이 많이 사라져 거의 모든 형태의 DML 을 지원한다.
ON절에 WHEN MATCHED THEN , WHEN NOT MATCHED THEN절을 이용하여 총 3가지 유형의 트랜잭션 처리
. WHEN MATCHED THEN -> UPDATE & DELETE
. WHEN NOT MATCHED THEN -> INSERT
MERGE 구문을 이용한 트랜잭션 유형별 수행 결과
135(118) 그림 넣기
136(119) 그림 넣기
CURSOR(Source Table)에서 데이터를 추줄하고, 추줄된 데이터 건수 만큼 FOR 문을 반복 수행하여, UPDATE & INSERT 구문을 수행하는데,
이러한 패턴의 프로그램은 보통 많은 데이터를 처리하는 배치 프로그램에서 사용한다.
추출되는 데이터 건수에 의해 결정되는 경우가 대부분인데 많은 데이터를 처리시 MERGE 구문을 활용하면, 수행시간을 크게 단축 시킬 수 있다.
MERGE 구문은 Oracle9i까지 UPSERT(UPDATE+INSERT)구문으로만 사용되었으나, lOg 부터는 Only UPDATE, Only INSER, UPSERT 등 다양한 패턴의, DML을 처리할 수 있도록 MERGE구문의 활용폭이 넓어져,
위와 같은 패터의 프로그램 성능을 개선하기 위해 사용되고 있다.
단,CASE(3)의 경우는 물리 파일을 읽어서 UPDATE 이나 INSERT 를 반복 수행하는 프로그램으로,Oracle DW 튜닝을 위해서 9i에 새로 나온 기능인 EXTERNAL TABLE 과 MERGE 구문을 함께 이용하면 성능을개선할수있다.