포스트

View Merging 으로 얻는 이익

View Merging 으로 얻는 이익

View Merging 은 inline view / view 를 메인 쿼리와 한 쿼리블록으로 통합하는 옵티마이저 변환입니다. View 가 사라지면 옵티마이저는 더 풍부한 후속 변환 (조건절 전이, JPPD, view merging 자체로 가능해진 join 제거 등) 을 적용할 수 있어, 동일한 쿼리도 훨씬 효율적인 plan 으로 풀립니다.

핵심 정리 (개념)

  1. 변환의 정의: inline view 또는 view 를 해체하여 메인 쿼리블록과 통합합니다. 변환 후에는 plan 에 VIEW operation 이 사라지고 모든 테이블이 한 레벨에서 join 됩니다.
  2. 세 가지 주요 이익:
    ① 추가 ORDER BY 생략 (인덱스로 대체 가능)
    ② 조건절 전이 (a.col = b.col + b.col = :va.col = :v 자동 추가)
    ③ 불필요한 join 제거 (PK 매칭으로 inner 가 1 row 보장될 때).
  3. 부가 효과: SQL 의 쿼리블록 수가 줄어 옵티마이저가 join 순서 / access path 결정 시 탐색 공간이 단순해져 더 좋은 plan 을 찾을 가능성이 커집니다.

View Merging 의 3 가지 이익

1) 추가 ORDER BY 생략

view 안의 ORDER BY 가 outer 쿼리의 정렬 요건과 동일하거나, outer 측에 정렬 키 인덱스가 존재하면 옵티마이저가 inline view 의 SORT 단계를 생략할 수 있습니다.

1
2
-- view merging 전:  SORT ORDER BY (view 내부) + outer 처리
-- view merging 후:  outer 의 인덱스 RANGE SCAN 만으로 정렬 자동 보장

2) 조건절 전이 (Predicate Transitive Closure)

a.col = b.colb.col = :v 두 조건이 함께 있으면, 옵티마이저는 동치성에 의해 a.col = :v 라는 새 조건을 자동으로 만들어 적용합니다. 이 변환은 view merging 으로 두 조건이 같은 쿼리블록에 모인 후 본격적으로 동작합니다.

1
2
3
4
5
6
7
8
-- view merging 전 (원래 작성한 쿼리):
WHERE a.dept_id = b.dept_id
  AND b.dept_id = :v_deptno

-- view merging 후 (옵티마이저 자동 추가):
WHERE a.dept_id = :v_deptno   ← 새 조건이 자동 생성
  AND b.dept_id = :v_deptno
  AND a.dept_id = b.dept_id

이 자동 생성된 조건이 outer 측 인덱스를 활용할 access path 를 옵티마이저에게 열어 줍니다.

3) 불필요한 Join 제거 (Join Elimination)

view 안에서 PK 또는 unique key 로 매칭되는 테이블이 SELECT list 에 사용되지 않으면, 옵티마이저는 그 join 자체를 제거할 수 있습니다. 메인 쿼리에서 view 의 어떤 컬럼을 참조하지 않으면 join 이 무의미하기 때문입니다.

이 변환도 view 가 살아 있으면 동작 안 함. view merging 이 선행 조건입니다.


시나리오

employeedepartment 를 PK / FK 로 join 하면서, department.department_id = :v_deptno 라는 inline view 를 만들어 사용하는 패턴입니다. 사람이 읽기에는 “department 를 먼저 좁히고 나서 employee 와 join” 이라는 흐름이지만, 옵티마이저는 view merging 으로 풀어버립니다.

테이블인덱스
employeeEMP_DEPARTMENT_IX (department_id)
departmentDEPT_ID_PK (department_id)

쿼리와 실행계획

1
2
3
4
5
6
SELECT a.employee_id, a.first_name, a.last_name, a.email, b.department_id
  FROM employee a,
       (SELECT b.department_id, b.department_name
          FROM department b
         WHERE b.department_id = :v_deptno) b
 WHERE a.department_id = b.department_id;

옵티마이저가 view merging 을 적용하면 plan 에 VIEW operation 이 사라지고, 모든 테이블이 메인 쿼리블록 레벨에서 join 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-------------------------------------------------------------------------------------------------
| Id | Operation                       | Name              | Rows | Bytes | Cost | Time         |
-------------------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT                |                   |      |       |    1 |              |
|  1 |  NESTED LOOPS                   |                   |    1 |    34 |    1 | 00:00:01     |
|  2 |   INDEX UNIQUE SCAN             | DEPT_ID_PK        |    1 |     4 |    0 |              |
|  3 |   TABLE ACCESS BY INDEX ROWID   | EMPLOYEE          |    1 |    30 |    1 | 00:00:01     |
|* 4 |    INDEX RANGE SCAN             | EMP_DEPARTMENT_IX |    1 |       |    0 |              |
-------------------------------------------------------------------------------------------------

Predicate Information:
----------------------
   2 - access("B"."DEPARTMENT_ID"=:V_DEPTNO)
   4 - access("A"."DEPARTMENT_ID"=:V_DEPTNO)
  • plan 에 VIEW operation 이 없음. view merging 이 적용된 결과로 inline view 가 사라졌습니다.
  • Id 4 의 access predicate 가 A.DEPARTMENT_ID=:V_DEPTNO 입니다. 원본 쿼리에서 이 조건은 A.DEPT_ID = B.DEPT_ID 였지만, 옵티마이저가 조건절 전이로 :V_DEPTNO 상수를 직접 employee 측 인덱스 access path 에 적용했습니다.
  • Id 2DEPT_ID_PK UNIQUE SCAN 으로 department 의 PK 에서 1 row 를 좁힌 뒤 NL 조인합니다.

분석

조건절 전이가 만든 변화

원본 쿼리의 두 조건:

1
2
A.department_id = B.department_id   (FROM 절 join 조건)
B.department_id = :v_deptno         (view 안의 필터)

view merging 으로 두 조건이 같은 쿼리블록에 모이면 옵티마이저는 동치성에 의해 새 조건을 자동 생성합니다.

1
A.department_id = :v_deptno         ← transitive closure 로 자동 추가

이 자동 생성 조건이 EMP_DEPARTMENT_IX (department_id) 인덱스의 access path 가 되어, employee 테이블에서 해당 부서원만 즉시 RANGE SCAN. view merging 이 없었다면 employee 풀 스캔 후 hash join 같은 비효율적 plan 이 됐을 가능성이 큽니다.

View Merging 이 차단되는 케이스

옵티마이저는 view merging 이 결과 의미를 바꿀 위험이 있는 경우 자동 적용을 보류합니다. 다음과 같은 view 는 일반적으로 merging 안 됩니다.

차단 사유예시
ROWNUM 사용view 안에 WHERE ROWNUM <= N 또는 ROWNUM SELECT
분석 함수 / 윈도우 함수OVER (PARTITION BY ...) 가 view 결과에 영향
일부 GROUP BY / DISTINCTgrouping key 가 outer join 조건과 호환 안 될 때
Set operationUNION / INTERSECT / MINUS view (단, UNION ALL 은 일부 가능)
NO_MERGE 힌트사용자가 명시 차단

이 차단 케이스에서는 옵티마이저의 다른 변환 (FPD, JPPD 등) 으로 우회 최적화를 시도합니다.

Simple View Merging vs Complex View Merging

Oracle 의 view merging 은 두 종류가 있습니다.

  • Simple View Merging (SVM): 단순한 projection + filter 만 있는 view 를 merge. 본 글의 케이스.
  • Complex View Merging (CVM): GROUP BY 또는 DISTINCT 가 있는 view 를 merge. outer 와 inner 의 GROUP BY 를 합쳐 단일 쿼리블록으로 풀어내는 더 복잡한 변환. _complex_view_merging 파라미터로 제어.

본 글의 시나리오는 SVM 입니다. CVM 은 결과 의미 보존 검증이 더 까다로워 옵티마이저가 cost 비교 후 보수적으로 적용합니다.

View Merging 은 옵티마이저 변환의 첫 단계 입니다. View 가 사라져야 옵티마이저가 조건절 전이, JPPD, join 제거 등 후속 변환을 적용할 수 있습니다. plan 에 VIEW operation 이 보이면 merging 이 차단됐다는 뜻이며, 이때는 view 안의 ROWNUM / DISTINCT / 분석 함수 / NO_MERGE 힌트 같은 차단 요인을 점검할 수 있습니다.

조건절 전이의 일반화

A.col = B.colB.col = :const 가 같은 쿼리블록에 있으면 A.col = :const 가 자동 추가되는 패턴은 SQL 의 동치성 규칙 에 따른 것이며, 옵티마이저의 Transitive Predicate Generation 단계에서 처리됩니다. 이 변환은 view merging 외에도 다음과 같은 환경에서 일어납니다.

  • 일반 inner join 의 조건과 추가 필터가 결합된 경우
  • 같은 쿼리블록 안의 여러 조건 사이

자동 변환이 안 일어날 때는 사용자가 직접 WHERE a.col = :v 를 추가하여 옵티마이저에게 access path 의 옵션을 늘려줄 수 있습니다.


정리

  1. View Merging 은 inline view / view 를 메인 쿼리와 한 쿼리블록으로 통합 하는 옵티마이저 변환입니다. plan 에 VIEW operation 이 사라지면 적용된 것입니다.
  2. 3 가지 이익: 추가 ORDER BY 생략, 조건절 전이로 새 access path 확보, 불필요 join 제거. 본 글 시나리오는 조건절 전이의 전형 사례 (A.dept_id = :V_DEPTNO 가 자동 추가됨).
  3. 차단 케이스: ROWNUM / 분석 함수 / 일부 GROUP BY / set operation / NO_MERGE 힌트. plan 에 VIEW 가 남으면 이런 차단 요인을 점검 후 제거하거나 옵티마이저의 다른 변환 (FPD, JPPD) 활용을 검토합니다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.