데이터베이스 옵티마이저는 쿼리 성능을 향상시키기 위해 사용자가 작성한 SQL을 내부적으로 다양한 형태로 변환하여 실행 계획을 최적화하는데 이러한 변환 기법들 중 하나가 바로 뷰 머징(View Merging)이다.
뷰 머징은 데이터베이스 옵티마이저가 쿼리 처리 방식을 결정하는 과정에서 중요한 역할을 하므로 이를 잘 활용하면 쿼리 성능을 획기적으로 개선할 수 있다.
뷰 머징(View Merging)이란?
뷰 머징은 데이터베이스 옵티마이저가 FROM
절에 있는 인라인 뷰(Inline View)의 경계를 허물고, 메인 쿼리와 뷰의 쿼리를 합쳐서 하나의 쿼리처럼 최적화하는 기법을 의미한다.
뷰 머징의 동작 방식 및 이점
뷰 머징이 이루어지면 옵티마이저는 마치 처음부터 하나의 쿼리였던 것처럼 전체를 분석하고 최적화한다. 이러한 동작 방식은 다음과 같은 여러 가지 성능상의 이점을 가져온다.
- 최적화된 실행 계획 수립: 메인 쿼리와 뷰의 조건이 결합되면서 옵티마이저는 조인 순서나 데이터 접근 방식을 더욱 다양하게 고려할 수 있게 된다. 이는 궁극적으로 최적의 실행 계획을 찾을 가능성을 높여준다.
- 불필요한 중간 결과 제거: 뷰를 별도로 처리하고 그 결과를 메인 쿼리가 다시 사용하는 방식은 불필요한 임시 결과 저장 단계를 유발할 수 있다. 뷰 머징은 이러한 중간 결과 저장 단계를 없애 메모리 사용량과 처리 시간을 감소시킨다.
- 술어 조건 푸시(Predicate Pushing)의 강력한 활용: 뷰 머징의 가장 큰 장점 중 하나는 술어 조건 푸시를 보다 완전하고 유연하게 적용할 수 있다는 점이다. 뷰의 경계가 사라지면 모든 조건이 메인 쿼리와 인라인 뷰 사이에 완전히 결합되어 최상의 액세스 경로와 조인 순서를 결정할 수 있게 된다. 이는 단순히 조건을 뷰 안으로 밀어 넣는 것을 넘어, 전체 쿼리의 재구성을 통해 전역적인 최적화를 가능하게 한다.
뷰 머징 유도 힌트 및 사용 예시
Oracle과 같은 일부 데이터베이스에서는 옵티마이저에게 뷰 머징을 강제로 유도하기 위한 힌트를 제공한다.
MERGE 힌트
MERGE 힌트는 옵티마이저에게 특정 뷰를 메인 쿼리와 강제로 머징하도록 지시한다.
예시:
SELECT /*+ MERGE(v) */ *
FROM emp e,
(SELECT deptno, dname FROM dept) v
WHERE e.deptno = v.deptno
AND v.dname = 'SALES';
위 쿼리에서 `/*+ MERGE(v) */` 힌트는 옵티마이저가 `v`라는 인라인 뷰를 메인 쿼리와 결합하여 최적의 실행 계획을 구성하도록 한다.
뷰 머징 전후 예시
다음은 뷰 머징이 적용되기 전후의 쿼리 형태를 비교한 예시이다.
뷰 머징 전
SELECT e.ename, v.dname
FROM emp e,
(SELECT deptno, dname FROM dept) v
WHERE e.deptno = v.deptno
AND v.dname = 'SALES';
뷰 머징 후
SELECT e.ename, d.dname
FROM emp e, dept d
WHERE e.deptno = d.deptno
AND d.dname = 'SALES';
뷰 머징 후 옵티마이저는 dept 테이블에서 dname 컬럼의 인덱스를 효과적으로 활용하여 'SALES' 부서를 빠르게 찾고, 이를 통해 emp 테이블과 효율적으로 조인할 수 있는 최적의 실행 계획을 구성한다.
뷰 머징이 불가능한 경우
뷰 머징은 쿼리 성능에 매우 유리하지만, 모든 경우에 적용되는 것은 아니다. 인라인 뷰 쿼리에 특정 구문이 포함되면 옵티마이저는 뷰의 결과를 먼저 독립적으로 완성해야만 메인 쿼리와의 관계를 결정할 수 있어 뷰를 합치지 못하고 구체화(Materialize)하여 사용한다.
- 집합 연산자 (UNION, UNION ALL 등): 두 개 이상의 결과 집합을 합치거나 비교하는 작업이 먼저 완료되어야 최종 결과 집합의 형태가 확정되므로 머징할 수 없다.
- GROUP BY / 집계 함수 (SUM, COUNT 등): 전체 데이터를 그룹핑하고 계산하는 작업이 먼저 끝나야 그룹별 집계 결과가 확정되므로 머징할 수 없다.
- 분석 함수/윈도우 함수 (ROW_NUMBER() 등): 전체 결과 집합 내에서 순위나 비율을 계산하므로, 전체 집합이 먼저 확정되어야 함수 값을 계산할 수 있어 머징할 수 없다.
- DISTINCT: 전체 데이터에서 중복을 제거하는 작업이 먼저 완료되어야 고유한 로우 집합이 확정되므로 머징할 수 없다.
- ROWNUM 의사컬럼:
ROWNUM
은 최종 결과 집합이 확정된 순서대로 번호를 부여한다. 만약 뷰를 머징하여 조인 순서나 접근 방식이 바뀌면ROWNUM
의 결과 자체가 달라지므로, 결과의 무결성을 위해 머징하지 않는다. - 계층형 쿼리 (CONNECT BY): 부모-자식 관계를 순차적으로 전개하는 작업이 먼저 완료되어야 전체 계층 구조가 확정되므로 머징할 수 없다.
뷰 머징 가능/불가능에 따른 차이점
구분 | 뷰 머징 가능 시 | 뷰 머징 불가능 시 (NO_MERGE / ROWNUM 등) |
---|---|---|
일반적인 경우 | 성능에 유리 (강력한 최적화 기능) | 성능에 불리 (최적화 기회 상실) |
장점 | 최적의 조인 순서/방식 선택, 술어 조건 푸시 가능 | - |
단점 | (거의 없음) | 조인 순서 고정, 비효율적인 실행 계획 수립 가능 |
예외적 사용 (NO_MERGE 힌트) | 함수 반복 호출 등 의도치 않은 부작용 발생 시 | 의도적으로 실행 순서를 고정시켜야 할 때 (예: 함수 호출 1회 보장) |
술어 조건 푸시 (Predicate Pushing)와 뷰 머징의 관계
술어 조건 푸시는 메인 쿼리의 조건을 인라인 뷰 내부로 전달하는 최적화 기법이다. 뷰 머징이 가능할 때 술어 조건 푸시는 더욱 강력하고 유연하게 동작한다.
술어 조건 푸시의 최적화 효과
- 완전한 결합과 재구성: 뷰와 메인 쿼리의 조건이 완전히 통합된다.
- 최적의 조인 순서 결정: 옵티마이저는 하나로 통합된 조건을 기반으로 최적의 조인 순서를 결정할 수 있다.
- 효율적인 인덱스 활용: 통합된 조건 덕분에 옵티마이저는 최적의 인덱스를 선택하여 빠르게 데이터를 조회한다.
1. 뷰 머징이 가능할 때 (가장 이상적인 경우)
이때의 '술어 조건 푸시'는 단순한 '밀어넣기'가 아니라 '완전한 결합과 재구성'을 의미한다. 뷰의 경계가 완전히 허물어지고, 메인 쿼리와 인라인 뷰의 모든 테이블과 조건절(WHERE
)이 하나의 거대한 SQL로 합쳐진다. 옵티마이저는 이 합쳐진 SQL 전체를 보고, 마치 처음부터 하나의 SQL이었던 것처럼 모든 최적화 기법을 총동원하여 최적의 조인 순서와 액세스 경로를 결정한다.
예시:
SELECT e.ename, v.dname
FROM emp e,
(SELECT deptno, dname FROM dept) v -- 단순 뷰 (머징 가능)
WHERE e.deptno = v.deptno
AND v.dname = 'SALES'; -- 메인 쿼리의 조건
뷰 머징이 일어나면 옵티마이저는 이 쿼리를 아래와 동일하게 취급한다.
SELECT e.ename, d.dname
FROM emp e, dept d
WHERE e.deptno = d.deptno
AND d.dname = 'SALES';
결과적으로 옵티마이저는 `dept` 테이블의 `dname` 인덱스를 먼저 사용하여 'SALES' 부서를 찾은 후 `emp` 테이블과 조인하는 최적의 계획을 세울 수 있다. 이것이 뷰 머징을 통해 실현된 가장 강력한 형태의 '술어 조건 푸시'이다.
2. 뷰 머징이 불가능할 때 (차선책)
GROUP BY
, ROWNUM
등이 있어 뷰 머징이 불가능할 때도, 옵티마이저는 성능을 조금이라도 개선하기 위해 '술어 조건 푸시'를 시도한다. 이때의 '술어 조건 푸시'는 제한적인 '밀어넣기'를 의미한다. 뷰의 경계는 그대로 둔 채, 메인 쿼리의 WHERE
절 조건을 인라인 뷰 안으로 복사해서 밀어 넣어준다. 뷰를 먼저 실행하되, 불필요한 데이터를 사전에 필터링하여 중간 결과 집합의 크기를 줄일 수 있다. 하지만 조인 순서를 바꾸거나 뷰 안의 테이블과 뷰 밖의 테이블 간의 액세스 경로를 자유롭게 최적화하지는 못한다.
예시:
SELECT e.ename, v.dname
FROM emp e,
(SELECT deptno, dname FROM dept GROUP BY deptno, dname) v -- GROUP BY로 머징 불가
WHERE e.deptno = v.deptno
AND v.deptno = 20; -- 메인 쿼리의 조건
이 경우 옵티마이저는 `v.deptno = 20` 조건을 뷰 안으로 밀어 넣어 내부적으로 아래와 같이 처리한다.
-- 1단계: 뷰 실행 (조건이 푸시됨)
(SELECT deptno, dname FROM dept WHERE deptno = 20 GROUP BY deptno, dname)
-- 2단계: 메인 쿼리와 조인
... JOIN (위 1단계 결과)
`dept` 테이블 전체를 `GROUP BY`하는 대신 `deptno=20`인 데이터만 먼저 거른 후 `GROUP BY`를 하므로 훨씬 효율적이다. 하지만 여전히 뷰를 먼저 실행해야 한다는 제약은 남아있다.
요약:
- 뷰 머징은 인라인 뷰의 벽을 허물어 쿼리를 하나로 합치는 기술이다.
- 이를 통해 '술어 조건 푸시'가 극대화되어 성능이 향상된다.
- GROUP BY, ROWNUM 등 특정 구문은 뷰 머징을 방해한다.
- 필요할 경우 NO_MERGE 힌트로 뷰 머징을 막을 수 있다.
결론
뷰 머징은 데이터베이스 쿼리 성능 최적화에 있어 매우 강력한 수단이다. 특히 술어 조건 푸시와 결합하여 쿼리 성능을 획기적으로 향상시킬 수 있다. 그러므로 뷰 사용이 많은 데이터베이스 환경에서는 뷰 머징이 활성화될 수 있는 쿼리 패턴을 이해하고, 필요한 경우 힌트 등을 적극적으로 활용하여 최적의 성능을 확보하는 전략을 잘 생각해야 할 것이다.
그러기 위해서 개발자는 언제 뷰 머징이 일어나고, 어떤 경우에 불가능한지를 이해하는 것이 중요할 것이다. 그렇게 된다면 비효율적인 쿼리의 원인을 분석하고, 때로는 NO_MERGE 힌트를 통해 옵티마이저의 행동을 직접 제어하여 최적의 성능을 이끌어내는 데 큰 도움이 될 것이라고 생각한다.
'DB' 카테고리의 다른 글
NL조인 기반 인덱스 설계 (0) | 2025.04.23 |
---|---|
같은 SELECT인데 결과가 0건(MyBatis) (0) | 2025.03.30 |
Index Skip Scan과 In-List 튜닝 (0) | 2025.02.16 |
My SQL 최적화 가이드 - 1. 최적화 (1) | 2024.11.28 |
MySQL) OPTIMIZE TABLE Statement (0) | 2024.09.20 |