기본 DML 튜닝

운구름·2022년 7월 10일
0
post-thumbnail
post-custom-banner

DML 성능에 영향을 미치는 요소

  • 인덱스
  • 무결성 제약
  • 조건절
  • 서브쿼리
  • Redo 로깅
  • Undo 로깅
  • Lock
  • 커밋

인덱스와 DML 성능

INSERT

테이블 레코드를 입력하면 인덱스에도 입력해야 한다.

테이블은 Freelist를 통해 블록을 할당받으나 인덱스는 정렬된 자료구조라 수직적 탐색을 통해 입력할 블록을 찾아야 한다.

인덱스에 입력하는 과정이 더 복잡하므로 DML 성능에 미치는 영향은 크다.

DELETE

테이블에서 레코드 하나 삭제시 인덱스 레코드를 모두 찾아서 삭제 해야한다.

UPDATE

변경된 컬럼을 참조하는 인덱스만 찾아서 변경해주면 된다.

대신, 테이블에서 한 건을 변경할 때마다 인덱스에는 두 개 오퍼레이션이 발생한다.

인덱스는 정렬된 자료구조이기 때문에 만약, A가 K로 변경되면 저장위치도 달라져서 삭제 후 삽입하는 방식으로 처리한다.

인덱스 갯수가 DML 성능에 미치는 영향이 매우 커서 인덱스 설계에 심혈을 기울여야 한다.

핵심 트랜잭션 테이블에서 인덱스 하나라도 줄이면 TPS는 그만큼 향상됨.

예시

  • source 테이블에는 레코드가 100만개임.
  • target 테이블은 비어있음.
  • target 테이블에 pk 인덱스 하나만 생성한 상태에서 source 테이블을 읽어 100만개 입력한다.
  • 결과 4.95초
create table source
as
select b.no, a.*
  from (select * from emp where rownum <= 10) a
     , (select rownum as no from dual connect by level <= 100000) b;

-------------------------------------------------
create table target
as
select * from source where 1 = 2;

-------------------------------------------------
alter table target add
constraint target_pk primary key(no, empno);
set timing on;
insert into target
select * from source;

100000 개의 행이 만들어졌습니다.
경 과 : 00.00.04.95
  • 인덱스 2개 생성 후 다시 100만건 입력한다
  • 결과는 38.98초가 될 정도로 엄청 느려짐. 이것이 인덱스 2개의 영향력
truncate table target;
create index target_x1 on target(ename);
create index target_x2 on target(deptno, mgr);
insert into target
select * from source;

100000 개의 행이 만들어졌습니다.
경 과 : 00.00.38.98

무결성 제약과 DML 성능

데이터 무결성 규칙은 아래 4가지가 있다.

  • 개체 무결성
  • 참조 무결성
  • 도메일 무결성
  • 사용자 정의 무결성

이들 규칙을 애플리케이션으로 구현할 수 있지만, DBML에서 PK, FK, Check, Not Null 같은 제약을 설정하면 완벽히 데이터 무결성을 지킬 수 있다.

PK, FK 제약은 Check, Not Null 제약보다 성능에 더 큰 영향을 미친다. Check, Not Null과 달리 PK, FK는 데이터를 실제로 조회해봐야 하기 때문.

앞의 테스트를 이어 일반 인덱스, PK 제약을 모두 제거하고 100만을 입력하면 속도가 빨라짐.

drop index target_x1;
drop index target_x2;
alter table target drop primary key;
truncate table target;

insert into target
select * from source;

100000개의 행이 만들어졌습니다.
경 과 : 00:00:01.32

조건절과 DML 성능

아래는 조건절만 포함하는 가장 기본적인 DML문과 실행계획이다.

set autotrace traceonly exp
update emp set sal = sal * 1.1 where deptno = 40;

delete from emp where deptno = 40;

서브쿼리와 DML 성능

아래는 서브쿼리를 포함하는 DML 문과 실행계획이다.

update emp e set sal = sal * 1.1
where exists 
   (select 'x' from dept where deptno = e.deptno and loc = 'CHICAGO');

delete from emp e
where exists 
  (select 'x' from dept where deptno = e.deptno and loc = 'CHICACO');

insert into emp
select e.*
  from emp_t e
 where exists
  (select 'x' from dept where deptno = e.deptno and loc = 'CHICAGO')

Redo 로깅과 DML 성능

오라클은 데이터파일과 컨트롤 파일에 가해지는 모든 변경사항을 Redo 로그에 기록

Redo 로그는 트랜잭션 데이터가 어떤 이유로 유실됐을 때, 트랜잭션을 재현하여 유실 이전 상태로 북구하는데 사용된다.

DML을 수행할 때마다 Redo 로그를 생성해야 해서 Redo 로깅이 DML 성능에 영향을 줌.

insert 작업에 Redo 로깅 생략 기능이 있는데 왜 제공 되는지 이유가 있다.

  • Redo 로그 용도
    1. DB Recovery
    2. Cash Recovery
    3. Fast Commit

Undo 로깅과 DML 성능

과거에 롤백이라는 용어를 사용함. 오라클 9i부터 Undo 라는 용어를 씀

Redo

  • 트랜잭션을 재현하여 과거를 현재 상태로 되돌림
  • 트랜잭션을 재현하는데 필요한 정보를 로깅함

Undo

  • 트랜잭션을 롤백함으로서 현재를 과거 상태로 되돌리는데 사용.
  • 변경된 블록을 이전상태로 되돌리는데 필요한 정보를 로깅함.

DML을 수행할때 Undo는 필수적으로 로깅이 되는데, 이것은 DML 성능에 영향을 미치나 Undo를 안남기는 방법은 없다.

Lock과 DML 성능

Lock은 DML 성능에 매우 크고 직접적인 영향을 미친다.

  • Lock을 필요이상으로 자주, 길게 사용하거나, 레벨이 높을수록 느려진다.
  • Lock을 너무 적게, 짧게 사용하면 데이터 품질이 나빠진다.
  • 그 사이를 잘 맞추려면 세심한 동시성 제어가 필요함

동시성 제어
동시에 실행되는 트랜잭션 수를 최대화하고 입력, 수정, 삭제, 검색시 데이터 무결성을 유지하기 위해 노력하는 것을 말한다.

커밋과 DML 성능

DML을 끝내려면 커밋까지 완료해야한다.

DML이 Lock에 의해 블로킹 된 경우 커밋은 DML 성능과 직결된다.

모든 DBMS가 Fast Commit을 구현하고 있다. 갱신한 데이터가 아무리 많아도 커밋은 빨리 처리한다.

Fast Commit의 도움으로 커밋을 순간적으로 처리하긴 하지만 커밋은 가벼운 작업이 아니다.

DB 버퍼캐시

DB에 접속한 사용자를 대신해 모든 일을 처리하는 서버프로세스는 버버캐시를 읽고 쓴다.

버퍼캐시에서 변경된 블록을 모아 주기적으로 데이터파일에 일괄 기록하는 작업은 DBWR 프로세스가 함.

Redo 로그 버퍼

버퍼캐시는 휘발성이라 DBWR 프로세스가 Dirty 블록들을 데이터파일에 반영할때까지 불안한 상태이다.

하지만 버퍼캐시에 가한 변경사항을 Redo 로그에도 기록하면 된다. 버퍼캐시 데이터가 유실되어도 Redo 로그를 이용해 언제든 복구할 수 있다.

Redo 로그도 파일이다.

디스크 I/O는 느리다. Redo 로깅 성능 문제를 해결하기 위해 오라클은 로그 버퍼를 사용.

⇒ Redo 로그 파일에 기록하기 전 먼저 로그버퍼에 기록하는 방식임. 로그버퍼에 기록되고 난 후 LGWR 프로세스가 Redo 로그파일에 일괄(Batch) 기록함.

트랜잭션 데이터 저장 과정

  1. DML 문을 실행하면 Redo 로그버퍼에 변경사항을 기록한다.
  2. 버퍼블록에서 데이터를 변경(레코드 추가/수정/삭제)한다. 물론 버퍼캐시에서 블록을 찾지 못하면 데이터파일에서 읽는 작업부터 한다.
  3. 커밋한다.
  4. LGWR 프로세스가 Redo 로그버퍼 내용을 로그파일에 일괄 저장한다.
  5. DBWR 프로세스가 변경된 버퍼블록들은 데이터파일에 일괄 저장한다.

오라클은 데이터를 변경하기 전에 항상 로그부터 기록한다.

서버 프로세스가 버퍼블록에서 데이터를 변경(②)하기 전에 Redo 로그버퍼에 로그를 먼저 기록(①)하는 이유다.

DBWR 프로세스가 Dirty 블록을 디스크에 기록(⑤)하기 전에 LGWR 프로세스가 Redo 로그파일에 로그를 먼저 기록(④)하는 이유이기도 하다.

이를 'Write Ahead Logging' 이라고 부른다.

잠자던 DBWR와 LGWR 프로세스는 '주기적으로'깨어나 각각 Dirty 블록과 Redo 로그버퍼를 파일에 기록한다.

LGWR 프로세스는 서버 프로세스가 커밋을 발행했다가 '신호를 보낼때도' 깨어나서 활동을 시작한다.

'적어도 커밋시점에는' Redo 로그버퍼 내용을 로그파일에 기록한다는 뜻이다.
이를 'Log Force at Commit' 이라고 부른다.

서버 프로세스가 변경한 버퍼블록들을 디스크에 기록하지 않았더라도 커밋 시점에 Redo 로그를 디스크에 안전하게 기록했다면, 그 순간부터 트랜잭션 영속성은 보장된다.

커밋 = 저장버튼

커밋은 서버 프로세스가 그때까지 했던 작업을 디스크에 기록하라는 명령어다.

저장을 완료할 때까지 서버 프로세스는 다음 작업을 진행할 수 없다.

Redo 로그버퍼에 기록된 내용을 디스크에 기록하도록 LGWR 프로세스에 신호를 보낸 후 작업을 완료했다는 신호를 받아야 다음 작업을 진행할 수 있다. Sync 방식.

LGWR 프로세스가 Redo 로그를 기록하는 작업은 디스크 I/O 작업이다.

그래서 커밋은 생각보다 느리다.

  • 트랜잭션을 필요 이상으로 길게 정의하여 오랫동안 커밋하지 않는 것은 문제
  • 너무 자주 커밋하는 것도 문제.

오래 커밋하지 않은 채 데이터를 계속 갱신하면 Undo 공간이 부족해져 시스템 장애 상황을 유발할 수 있음.

루프를 돌면서 건건이 커밋한다면, 프로그램 자체 성능이 매우 느려짐.

트랜잭션을 논리적으로 잘 정의함으로써 불필요한 커밋이 발생하지 않도록 구현해야 한다.

2022년 6월 20일

데이터베이스 Call과 성능

SQL은 3단계로 나누어 실행된다.

  • Parse Call
    • SQL 파싱과 최적화를 수행하는 단계이다. SQL과 실행계획 라이브러리 캐시에서 찾으면 최적화 단계는 생략할 수 있다.
  • Execute Call
    • 말그대로 SQL을 실행하는 단계이다. DML은 이 단계에서 모든 과정이 끝나지만 SELECT 문은 Fetch 단계이다.
  • Fetch Call
    • 데이터를 읽어서 사용자에게 결과집합을 전송하는 과정으,로 SELECT문에서만 나타난다. 전송할 데이터가 많을 때는 Fetch Call로 나눌수 있다.

Call이 어디서 발생하느냐에 따라 User Call과 Recursive Call로 나눌 수 있다.

User Call

User Call은 네트워크를 경유해 DBMS 외부로부터 인입되는 Call이다.

DBMS 입장에서 사용자는 WAS이다. 3Tier 아키텍처에서 User Call은 WAS 서버에서 발생하는 Call이다.

Recursive Call

DBMS 내부에서 발생하는 Call

SQL 파싱과 최적화 과정에서 발생하는 데이터 딕셔너리 조회, PL/SQL로 작성한 사용자 정의 함수, 프로시저, 트리거에 내장된 SQL을 실행할 때 발생하는 Call이다.

위의 두 Call은 Parse, Execute, Fetch 단계를 거친다. 데이터베이스는 Call이 많을수록 성능은 느릴 수 밖에 없다. User Call이 성능에 미치는 영향은 매우 크다.

절차적 루프 처리

create table source
as
select b.no, a.*
from (select * from emp where rownum <= 10) a,
	(select rownum as no from dual connect by level <= 100000) b;

create table target
as 
select * from source where 1 = 2;
  • 생성한 source 테이블에 레코드 100만개 입력함.
  • source 테이블을 읽어 100만번 루프 돌면서 target 테이블에 입력
set timing on

begin
	for s in (select * from source)
	loop
		insert into target values ( s.no, s.empno, s.ename, 
																s.job, s.mgr, s.hiredate, 
																s.sal, s.comm, s.deptno );
	end loop;

	commit;
end;

경    과 : 00:00:29:31

루프를 돌면서 건건히 call이 발생하지만, 네트워크를 경유하지 않은 Recursive Call이므로 29초만에 수행을 마침.

One SQL의 중요성

insert into target
	select * from source;

-- 01.46초

업무로직이 복잡해지면 절차적으로 처리할 수 밖에 없지만, 그렇지 않으면 One SQL로 구현하는게 좋다.

  • Insert Into Select
  • 수정 가능 조인 뷰
  • Merge 문

Array Processing 활용

One SQL은 구현하기 힘든데 Array Processing 쓰면 One SQL 안쓰고도 Call의 부하를 줄일 수 있다.

Array Processing 기능을 활용하면 한 번의 SQL (INSERT/UPDATE/DELETE) 다량의 레코드를 동시에 처리할 수 있다.

이는 네트워크를 통한 데이터베이스 Call을 줄이고, 궁극적으로 SQL 수행시간과 CPU 사용량을 획기적으로 줄여준다.

인덱스 및 제약 해제를 통한 대량 DML 튜닝

인덱스와 무결성 제약 조건은 DML 성능에 큰 영향을 끼친다.

근데 OLTP에서 이들 기능을 해제할 수 없다. 하지만 Batch 프로그램에서는 이 기능을 해제해서 큰 성능개선 효과를 얻을 수 있다.

책 예시 : 1000만건 입력하기

  • PK(Unique, index) + 일반 인덱스 : 1분 19초

PK 제약과 인덱스 해제 1 - PK 제약에 Unique 인덱스를 사용한 경우

  • PK 제거 + 일반 인덱스 Unusable : 5.84초
  • 데이터 1000만건 있는 상태에서 PK 활성화 : 6.77초
  • 데이터 1000만건 있는 상태에서 일반 인덱스 활성화 : 8.26초

총 : 20.87초

PK 제약과 인덱스 해제 2 - PK 제약에 Non-Unique 인덱스를 사용한 경우

  • PK Unique 제거 + PK index Unusable + x1 index Unusable : 5.53초
  • 데이터 1000만건 있는 상태에서 일반 인덱스 활성화 : 7.24초
  • PK 인덱스 Unique 활성화 : 0.06초

총 : 18.1초

  • 데이터 1000만건 있는 상태에서 PK 활성화 : 5.27초

수정가능 조인 뷰

전통적인 방식 UPDATE

다음과 같이 첫번째 쿼리를 두번째 쿼리처럼 고칠 수 있다.

하지만 아래의 쿼리들 모두 전통적인 update 방식이라 완전한 비효율을 없앨 수 없다.

update 고객 c
set 최종거래일시 = (select max(거래일시) from   거래
                     where  고객번호 = c.고객번호
                     and 거래일시 >= trunc(add_months(sysdate, -1)))
  , 최근거래횟수 = (select count(*) from   거래
                     where  고객번호 = c.고객번호
                     and 거래일시 >= trunc(add_months(sysdate, -1)))
  , 최근거래금액 = (select sum(거래금액) from   거래
                     where  고객번호 = c.고객번호
                     and 거래일시 >= trunc(add_months(sysdate, -1)))
where exists (select 'x' from 거래 
               where 고객번호 = c.고객번호
               and 거래일시 >= trunc(add_months(sysdate, -1)));
update 고객 c
set (최종거래일시, 최근거래횟수, 최근거래금액) =
    (select max(거래일시), count(*), sum(거래금액)
     from 거래
     where 고객번호 = c.고객번호
     and 거래일시 >= trunc(add_months(sysdate, -1)))
where exists (select 'x' from 거래
              where 고객번호 = c.고객번호
              and 거래일시 >= trunc(add_months(sysdate, -1)));
update 고객 c
set (최종거래일시, 최근거래횟수, 최근거래금액) =
    (select max(거래일시), count(*), sum(거래금액)
     from 거래
     where 고객번호 = c.고객번호
     and 거래일시 >= trunc(add_months(sysdate, -1)))
where exists (select **/*+ unnest hash_sj */** 'x' from 거래
              where 고객번호 = c.고객번호
              and 거래일시 >= trunc(add_months(sysdate, -1)));
update 고객 c
set (최종거래일시, 최근거래횟수, 최근거래금액 ) =
     (select nvl(max(거래일시), c.최종거래일시)
            ,decode(count(*), 0, c.최근거래횟수, count(*))
            ,nvl(sum(거래금액), c.최근거래금액)
      from 거래
      where 고객번호 = c.고객번호
      and 거래일시 >= trunc(add_months(sysdate, -1)));

수정가능한 조인 뷰

  • 조인뷰는 from절에 두 개 이상 테이블을 가진 뷰를 가리키며, 수정 가능 조인 뷰는 입력, 수정, 삭제가 허용되는 조인 뷰를 말한다.
  • 1쪽 집합과 조인되는 M쪽 집합에만 입력, 수정, 삭제가 허용된다.
  • 수정 가능 조인 뷰를 활용하면 전통적인 방식의 update문에서 참조 테이블을 두번 조인하는 비효율을 없앨 수 있다.
  • 예시1
    update
    (select /*+ ordered use_hash(c) no_merge(t) */
             c.최종거래일시,  c.최근거래횟수, c.최근거래금액
              , t.거래일시,  t.거래횟수, t.거래금액
    from (select 고객번호, max(거래일시) 거래일시, count(*) 거래횟수, sum(거래금액) 거래금액
             from 거래
             where 거래일시 >= trunc(add_months(sysdate, -1))
            group by 고객번호) t, 고객 c
    where  c.고객번호 = t.고객번호
    )
    set 최종거래일시 = 거래일시
      , 최근거래횟수 = 거래횟수
      , 최근거래금액 = 거래금액 ;
  • 예시2 아래와 같이 조인뷰를 통해 job = ‘CLERK’ 인 레코드 loc을 모두 ‘SEOUL’로 변경을 허용하면? job = ‘CLERK’인 사원 뿐만 아니라 다른 job을 가진 사원의 부서 소재지까지 바뀐다. (개판!)
    create table emp as select * from scott.emp;
    create table dept as select * from scott.dept;
    
    select e.rowid emp_rid, e.*, d.rowid dept_rid, d.dname, d.loc
    from emp e, dept d
    where e.deptno = d.deptno;
    
    update emp_dept_view set loc = 'SEOUL' where job = 'CLERK'
    
    EMPNO ENAME      JOB              SAL     DEPTNO DNAME          LOC
    ---------- ---------- --------- ---------- ---------- -------------- -------------
          7902 FORD       ANALYST         3000         20 RESEARCH       DALLAS
          7788 SCOTT      ANALYST         3000         20 RESEARCH       DALLAS
          7934 MILLER     **CLERK**           1300         **10** ACCOUNTING     NEW YORK
          7369 SMITH      **CLERK**            800         **20** RESEARCH       DALLAS
          7876 ADAMS      **CLERK**           1100         **20** RESEARCH       DALLAS
          7900 JAMES      **CLERK**            950         **30** SALES          CHICAGO
          7782 CLARK      MANAGER         2450         10 ACCOUNTING     NEW YORK
          7566 JONES      MANAGER         2975         20 RESEARCH       DALLAS
          7698 BLAKE      MANAGER         2850         30 SALES          CHICAGO
          7839 KING       PRESIDENT       5000         10 ACCOUNTING     NEW YORK
          7654 MARTIN     SALESMAN        1250         30 SALES          CHICAGO
          7844 TURNER     SALESMAN        1500         30 SALES          CHICAGO
          7521 WARD       SALESMAN        1250         30 SALES          CHICAGO
          7499 ALLEN      SALESMAN        1600         30 SALES          CHICAGO
  • 예시3 아래 쿼리는 문제가 없어 보이지만 에러가 발생한다. 이유는 옵티마이저가 어느 테이블이 1쪽 집합인지 알 수 없기 때문이다. 이것은 DELETE, INSERT도 마찬가지다.
    update emp_dept_view set comm = nvl(comm, 0) + (sal * 0.1) where sal <= 1500
    
    => ORA-01779: 키-보존된것이 아닌 테이블로 대응한 열을 수정할 수 없습니다.
  • 올바른 예시 아래와 같이 Unique 인덱스를 생성해야 수정 가능 조인 뷰를 통한 입력, 수정, 삭제가 가능하다.

    이와 같이 PK 제약을 설정하면 EMP 테이블은 ‘키-보존 테이블’이 되고 DEPT는 ‘비 키-보존 테이블’이 된다.

    alter table dept add constraint dept_pk primary key(deptno);
    
    update emp_dept_view set comm = nvl(comm, 0) + (sal * 0.1) where sal <= 1500;

키보존 테이블

키 보존 테이블이란, 조인된 결과 집합을 통해서도 중복 값 없이 unique하게 식별히 가능한 테이블,그리고 뷰에 rowid를 제공하는 테이블이다.

ORA-01779

  • 11g 이하 버전에서 아래 1번쿼리 실행시 ORA-01779이 발생
  • 이유는 EMP 테이블을 DEPTNO로 Group by 해서 DEPTNO 컬럼으로 조인한 DEPT 테이블은 키가 보존되는데 옵티마이저가 불필요한 제약을 가한 것.
  • 10g 버전에서는 bypass_ujvc 힌트를 주면 제약을 피할 수 있다.
    (Updatable join view check 생략 지시 힌트)
  • 11g 버전부터 이 힌트 사용불가. ⇒ Merge 문으로 바꿔줘야함.
-- 1번 쿼리
update
(select d.deptno, d.avg_sal d_avg_sal, e.avg_sal e_avg_sal
from (select deptno, round(avg(sal), 2) avg_sal from emp group by deptno) e
      , dept d
where d.deptno = e.deptno)
set d_avg_sal = e_avg_sal;

**> ORA-01779: 키-보존된것이 아닌 테이블로 대응한 열을 수정할 수 없습니다**

-- 2번 쿼리 (bypass_ujvc 힌트)
update /*+ bypass_ujvc */
(select d.deptno, d.avg_sal d_avg_sal, e.avg_sal e_avg_sal
 from (select deptno, round(avg(sal), 2) avg_sal from emp group by deptno) e
       , dept d
where d.deptno = e.deptno)
set d_avg_sal = e_avg_sal;

Merge문 활용

DW에서 가장 흔히 발생하는 오퍼레이션은 기간계 시스템에서 가져온 신규 트랜잭션 데이터를 반영함으로써 두 시스템간 데이터를 동기화 하는 작업이다.

DW에 데이터 적재 작업을 효과적으로 지원하게 위해 오라클 9i부터 merge 문이 도입됨.

  • 전일 발생한 변경 데이터를 기간계 시스템으로 부터 추출 (Extraction)
    create table customer_delta
    as
    select * from customer
    where mod_dt >= trunc(sysdate)-1
    and   mod_dt <  trunc(sysdate);
  • CUSTOMER_DELTA 테이블을 DW시스템으로 전송 (Transportation)
  • DW 시스템으로 적재 (Loading)
    merge into customer t using customer_delta s on (t.cust_id = s.cust_id)
    when matched then update
      set t.cust_nm = s.cust_nm, t.email = s.email, ...
    when not matched then insert
      (cust_id, cust_nm, email, tel_no, region, addr, reg_dt) values
      (s.cust_id, s.cust_nm, s.email, s.tel_no, s.region, s.addr, s.reg_dt);

Optional Clauses

update와 insert를 선택적으로 처리할 수 있다.

merge into customer t using customer_delta s on (t.cust_id = s.cust_id)
**when matched then update**
  set t.cust_nm = s.cust_nm, t.email = s.email, ...;

merge into customer t using customer_delta s on (t.cust_id = s.cust_id)
**when not matched then insert**
  (cust_id, cust_nm, email, tel_no, region, addr, reg_dt) values
  (s.cust_id, s.cust_nm, s.email, s.tel_no, s.region, s.addr, s.reg_dt);

Conditional Operations

on절에 기술한 조인문 외에 추가로 조건절을 기술할 수도 있다.

merge into customer t using customer_delta s on (t.cust_id = s.cust_id)
when matched then update
  set t.cust_id = s.cust_id, t.cust_nm = s.cust_nm, t.email = s.email, ...
  **where reg_dt >= to_char('20000101','yyyymmdd')**
when not matched then insert
  (cust_id, cust_nm, email, tel_no, region, addr, reg_dt) values
  (s.cust_id, s.cust_nm, s.email, s.tel_no, s.region, s.addr, s.reg_dt)
  **where reg_dt < trunc(sysdate);**

DELETE Clause

  • 이미 저장된 데이터를 조건에 따라 지우는 기능도 제공한다.
  • 아래의 merge문에서 update가 이루어진 결과로서 탈퇴일시(withdraw_dt)가 null이 아닌 레코드만 삭제된다.
  • 탈퇴일시가 null이 아니었어도 merge문을 수행한 결과가 null이면 삭제하지 않는다.
merge into customer t using customer_delta s on (t.cust_id = s.cust_id)
when matched then update
  set t.cust_id = s.cust_id, t.cust_nm = s.cust_nm, t.email = s.email, ...
  **delete where t.withdraw_dt is not null** --탈퇴일시가 null이 아닌 레코드 삭제
when not matched then insert
  (cust_id, cust_nm, email, tel_no, region, addr, reg_dt) values
  (s.cust_id, s.cust_nm, s.email, s.tel_no, s.region, s.addr, s.reg_dt);

Merge Into 활용

  • SQL 수행 빈도 개선
    • 저장하려는 레코드가 기존에 있던 것이면 update를 수행
    • 그렇지 않으면 insert를 수행하는 경우, SQL이 항상 두번씩 수행
      (select 한번, insert 또는 update 한 번)
    • merge 문을 활용하면 SQL이 한번만 수행된다
  • 논리I/O발생을 감소하여 SQL 수행 속도 개선
profile
뭉실뭉실 코더 운구름
post-custom-banner

0개의 댓글