[Oracle] 임시 테이블 트랜잭션 GTT와 세션 GTT / WITH문을 활용한 SubQuery 재사용

EUN JY·2022년 4월 14일
1

Database

목록 보기
4/21

1. GTT(Global Temporary Table, 전역 임시 테이블)

  • Oracle에서 사용하는 임시 테이블을 Global Temporary 테이블이라고 함
  • 생성 방법에 따라 트랜잭션 GTT, 세션 GTT로 구분
  • 트랜잭션 GTT : 같은 트랜잭션 내에서만 데이터가 유지
  • 세션 GTT : 같은 세션 내에서 데이터가 유지

1-1. 트랜잭션 GTT

  • 트랜잭션이 살아 있는 동안에만 데이터가 유지됨
  • CREATE 다음에 "GLOBAL TEMPORARY", 맨 마지막에 "ON COMMIT DELETE ROWS" 구문을 추가
  • ON COMMIT DELETE ROWS : COMMIT 시에 row를 삭제하라는 의미
    • DML로 GTT 테이블에 데이터를 생성, 수정할 당시에는 데이터가 남아 있지만 COMMIT을 실행하면 모든 데이터(Row)가 없어짐
    • 생략이 가능하므로 이 구문을 빼고 GTT를 생성하면 디폴트로 트랜잭션 GTT가 만들어짐
CREATE GLOBAL TEMPORARY TABLE 테이블명 (
    컬럼1 데이터타입, ...
)
[ON COMMIT DELETE ROWS];

1-1-1. 트랜잭션 GTT 예제

  • 아래와 같이 테이블 생성
CREATE GLOBAL TEMPORARY TABLE TX_GTT (
    NO      NUMBER,
    NAME    VARCHAR(20)
)
ON COMMIT DELETE ROWS;
  • 데이터 INSERT 후, COMMIT 전후의 데이터 유지 여부 확인하기
DECLARE
    txGttCnt NUMBER;
BEGIN
    INSERT INTO TX_GTT (NO, NAME)
    SELECT 1, 'Kate' FROM DUAL
    UNION ALL 
    SELECT 2, 'Bob' FROM DUAL
    UNION ALL
    SELECT 3, 'Alex' FROM DUAL
    ;
    SELECT COUNT(1) INTO txGttCnt FROM TX_GTT;
    dbms_output.put_line('txGttCnt : ' || txGttCnt);
    COMMIT;
    SELECT COUNT(1) INTO txGttCnt FROM TX_GTT;
    dbms_output.put_line('txGttCnt : ' || txGttCnt);
END
;

/* 결과
txGttCnt : 6
txGttCnt : 0
*/

1-2. 세션 GTT

  • 같은 세션 내에 있는 한 데이터가 유지됨
  • ON COMMIT PRESERVE ROWS는 COMMIT을 실행한 후에도 데이터(로우)를 보존하라는 뜻
  • COMMIT 여부에 상관없이 같은 세션에서 데이터가 보존됨(다른 세션에 있는 사용자는 이 데이터를 공유할 수 없음)
    • 세션에서만 데이터가 공유되며, 세션을 종료하면 데이터는 사라짐
    • 테이블 이름 앞에 '#'이 붙는 MSSQL의 임시 테이블과 같은 특성을 가짐
CREATE GLOBAL TEMPORARY TABLE 테이블명 (
    컬럼1 데이터타입, ...
)
ON COMMIT PRESERVE ROWS;

1-2-1. 세션 GTT 예제

  • 아래와 같이 테이블 생성
CREATE GLOBAL TEMPORARY TABLE SS_GTT (
    NO      NUMBER,
    NAME    VARCHAR(20)
)
ON COMMIT PRESERVE ROWS;
  • 데이터 INSERT 후, COMMIT 전후의 데이터 유지 여부 확인하기
DECLARE
    ssGttCnt NUMBER;
BEGIN
    INSERT INTO SS_GTT
    SELECT 1, 'Alex' FROM DUAL
    UNION ALL 
    SELECT 2, 'Rose' FROM DUAL
    UNION ALL 
    SELECT 3, 'Peter' FROM DUAL;
    SELECT COUNT(1) INTO ssGttCnt FROM SS_GTT;
    dbms_output.put_line('ssGttCnt : ' || ssGttCnt);
    COMMIT;
    SELECT COUNT(1) INTO ssGttCnt FROM SS_GTT;
    dbms_output.put_line('ssGttCnt : ' || ssGttCnt);
END;

/* 결과
ssGttCnt : 6
ssGttCnt : 6
*/

1-3. GTT의 특징

  • GTT의 한계
    • 파티션 GTT를 만들 수 없음
    • GTT에 인덱스는 만들 수 있으나 외래키를 만들 수 없음
    • 병렬로 UPDATE, DELETE, MERGE 문을 실행할 수 없음
    • GTT 컬럼으로는 중첩 테이블 타입을 사용할 수 없음
  • GTT의 활용

       여러 개의 테이블을 조인해서 복잡한 연산을 수행한 결과를 보여주는 리포트를 만들어야 하는데 해당 결과를 산출하기에는 단일 SELECT문으로 구현하기가 어렵다고 하자. 이럴 때, 과거에는 최종 리포트 구조에 맞게 테이블을 만들고 프로시저 안에서 여러 단계에 걸쳐 데이터를 입력하고 조작해 원하는 결과를 만들었다. 이렇게 하면 리포트 화면에서는 WHERE 조건도 필요 없는 단순 SELECT문만 실행하면 됐다.
      하지만 이 방법은, 다른 세션의 사용자가 거의 동시에 같은 프로시저를 실행하고 결과를 조회하면 데이터가 중복되거나 누락되는 등의 문제가 발생할 소지가 있다. 하지만 GTT를 사용하면 데이터가 세션별로 관리되므로 데이터가 꼬이는 현상이 발생할 가능성은 거의 없을 뿐만 아니라 원하던 기능도 구현할 수 있다.

2. WITH 문

  • 이름을 가진 SubQuery Block을 정의한 후 사용하는 구문
  • Query의 전체적인 가독성을 높이고, 재사용할 수 있음
    • 오라클 공유 메모리에 임시 테이블을 생성하여 반복 재사용이 가능하도록 함
    • 자주 실행되는 경우 한 번만 Parsing되고 Plan 계획이 수립됨
    • 쿼리의 성능 향상 : 동일 테이블 접근을 최소화하며 메모리에 생성된 임시 테이블에서 필요한 데이터를 메모리로 접근하기 때문에 디스크 IO로 테이블에 접근하는 것보다 효율적
  • 계층형 쿼리 구현 가능
  • 대부분의 DBMS에서 지원, 모든 DML에서 사용 가능
  • 참고) UNION ALL, DECODE/CASE, WITH, ROLLUP/Grouping Sets 문장들과 서로 형태 변환이 가능하여 함께 쓰면 성능상 유리

2-1. WITH 문 구조

  • WITH [별명] AS (SUB QUERY)
  • 컬럼명은 생략할 수 있음
  • 쉼표(,)로 구분하여 여러개를 정의 가능
  • 먼저 생성된 SubQuery는 나중에 생성하는 SubQuery에서 사용할 수 있음
    • 예) [별명2]에서 [별명1]을 사용할 수 있음.
WITH [별명1] [(컬럼명1 [,컬럼명2])] AS (
    SUB QUERY
) [,별명2 AS ...]
MAIN QUERY
  • Oracle에서는 한 번만 사용되면 Inline View, 두 번 이상 사용되면 Materialize View로 처리함
    • 한번도 사용하지 않으면 [ORA-01762] 오류 발생
    • /+ Materialize / 힌트로 Inline View를 Materialize View로 만들 수 있음

2-2. WITH 문 예제

WITH TEMP_USER_INFO_SCORE AS 
(
    SELECT I.USER_NUM, I.USER_NM, I.USER_BIRTH, S.USER_GRADE
    FROM USER_INFO I
    LEFT JOIN USER_SCORE S
    ON I.USER_NUM = S.USER_NUM
)
SELECT 
    TEMP.USER_NUM
    , TEMP.USER_NM
    , TEMP.USER_BIRTH
    , TEMP.USER_GRADE
FROM TEMP_USER_INFO_SCORE TEMP
WHERE USER_GRADE <> 'A'
;
profile
개린이

0개의 댓글