View Merging 으로 얻는 이익
View Merging 은 inline view / view 를 메인 쿼리와 한 쿼리블록으로 통합하는 옵티마이저 변환입니다. View 가 사라지면 옵티마이저는 더 풍부한 후속 변환 (조건절 전이, JPPD, view merging 자체로 가능해진 join 제거 등) 을 적용할 수 있어, 동일한 쿼리도 훨씬 효율적인 plan 으로 풀립니다.
핵심 정리 (개념)
- 변환의 정의: inline view 또는 view 를 해체하여 메인 쿼리블록과 통합합니다. 변환 후에는 plan 에
VIEWoperation 이 사라지고 모든 테이블이 한 레벨에서 join 됩니다. - 세 가지 주요 이익:
① 추가 ORDER BY 생략 (인덱스로 대체 가능)
② 조건절 전이 (a.col = b.col + b.col = :v→a.col = :v자동 추가)
③ 불필요한 join 제거 (PK 매칭으로 inner 가 1 row 보장될 때). - 부가 효과: 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.col 과 b.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 이 선행 조건입니다.
시나리오
employee 와 department 를 PK / FK 로 join 하면서, department.department_id = :v_deptno 라는 inline view 를 만들어 사용하는 패턴입니다. 사람이 읽기에는 “department 를 먼저 좁히고 나서 employee 와 join” 이라는 흐름이지만, 옵티마이저는 view merging 으로 풀어버립니다.
| 테이블 | 인덱스 |
|---|---|
employee | EMP_DEPARTMENT_IX (department_id) |
department | DEPT_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 에
VIEWoperation 이 없음. 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 2의DEPT_ID_PKUNIQUE 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 / DISTINCT | grouping key 가 outer join 조건과 호환 안 될 때 |
| Set operation | UNION / 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 에
VIEWoperation 이 보이면 merging 이 차단됐다는 뜻이며, 이때는 view 안의 ROWNUM / DISTINCT / 분석 함수 / NO_MERGE 힌트 같은 차단 요인을 점검할 수 있습니다.
조건절 전이의 일반화
A.col = B.col 과 B.col = :const 가 같은 쿼리블록에 있으면 A.col = :const 가 자동 추가되는 패턴은 SQL 의 동치성 규칙 에 따른 것이며, 옵티마이저의 Transitive Predicate Generation 단계에서 처리됩니다. 이 변환은 view merging 외에도 다음과 같은 환경에서 일어납니다.
- 일반 inner join 의 조건과 추가 필터가 결합된 경우
- 같은 쿼리블록 안의 여러 조건 사이
자동 변환이 안 일어날 때는 사용자가 직접 WHERE a.col = :v 를 추가하여 옵티마이저에게 access path 의 옵션을 늘려줄 수 있습니다.
정리
- View Merging 은 inline view / view 를 메인 쿼리와 한 쿼리블록으로 통합 하는 옵티마이저 변환입니다. plan 에
VIEWoperation 이 사라지면 적용된 것입니다. - 3 가지 이익: 추가 ORDER BY 생략, 조건절 전이로 새 access path 확보, 불필요 join 제거. 본 글 시나리오는 조건절 전이의 전형 사례 (
A.dept_id = :V_DEPTNO가 자동 추가됨). - 차단 케이스: ROWNUM / 분석 함수 / 일부 GROUP BY / set operation /
NO_MERGE힌트. plan 에VIEW가 남으면 이런 차단 요인을 점검 후 제거하거나 옵티마이저의 다른 변환 (FPD, JPPD) 활용을 검토합니다.