[DB] 쿼리 변환 - Unnesting

최민석·2021년 7월 26일
2

쿼리 변환이란?

SQL은 구조화된 질의이다.
말 그대로 언어의 한 종류이기에 같은 목적이라도 여러방식으로 표현이 가능하다
예를 들어보자.

<Query1>
select *
from dept d
where  not exists(select 'x'
  	           from emp
               where deptno = d.deptno)
            
<Query2>
select d.*
from dept d, emp e
where e.deptno(+) = d.deptno
and e.rowid is null
         

위 두 쿼리는 결과는 같지만 성능은 다를수 있다.
쿼리 변환이 작동하지 않을때만 말이다.

언제?

비용기반 옵티마이저는 서브엔진 으로써 존재하는 Query Transformer가
Parser와 ExecutionPlan 단계 사이에서 쿼리변환을 실시한다.

어떻게?

서브쿼리Unnesting이란 중첩을 부정한다는 의미로 쿼리내에 중첩된 서브쿼리를 풀어낸다.
대다수의 서브쿼리를 조인형태로 변환이 가능한데, 조인형태의 쿼리를 좀 더 다양한 경로, 테크닉을 활용하여 더 나은 실행계획을 찾을 가능성이 높아진다.

select * form emp
where deptno in (select deptno from dept)

위와같은 쿼리를 Unnesting 하지않고 옵티마이저가 최적화 할떄는
메인쿼리에서 읽히는 레코드마다 값을 넘기면서 서브쿼리를 반복수행한다.
그리고 조건절에 의해 필터 방식으로 실행계획을 수립하므로 비효율이 발생한다.

반대로 unnest 힌트를 사용하거나 옵티마이저가 스스로 Unnesting할때는 아래와 같이 조인문의 형태로 변환된다.

select * 
from (select deptno from dept) a, emp b
where d.deptno = a.deptno

이것은 다시 나중에 설명하는 뷰Merging 과정을 거쳐 아래와 같은 최종 형태로 변환된다.

select emp.* from dept,emp
where emp.deptno = dept.deptno

서브쿼리로 시작된 쿼리문이 일반적인 NL조인의 형태를 띄며 액세스 방식으로 조건을 검사하므로 비효율이 거의 없다.

  • Unnesting된 조인문은 어느쪽이든 드라이빙 집합으로 선택될 수 있다.
  • 만약드라이빙 테이블을 컨트롤할땐 leading 힌트를 사용하면 된다.
select /*+leading(emp) */ * from emp
where deptno in (select /*+ unnset nl_sj*/ deptno from dept)

위에서 나온 nl_sj 힌트는 NL세미조인을 유도하며 세미조인은 아래와 같은 알고리즘으로 실행된다.

for(i=0; ; i++){
    for(k=0; ; k++){
    	if(i==k) break;
    }
}

즉 드라이빙 테이블에서 조건을 성립되는 이너테이블을 만나자마자 다음 레코드로 넘어가기 떄문에 비효율이 없다.
즉 결과 집합은 같으면서 서브쿼리를 풀어내고, 더 좋은 실행계획(세미조인)을 찾는것이 쿼리변환 - Unnesting의 목적이다.

Unnesting 하지않으면 망한 쿼리인가?

Unnesting하지않을때, 필터 최적화 기법을 가지고 있는데 서브쿼리의 결과를 캐싱해놓고 계속 비교하며 효율을 올리는 것이다.
이러한 기법으로 no_unnest일때도 열심히 효율을 위해 필터링 하지만,
이는 세미조인에서도 캐싱되는 기법이다 (10g부터)
즉 서브쿼리는 최대한 풀어내어 조인등의 과정을 고려하며 계획을 세워야 유리하다.

profile
🔥🔥🔥🔥 G U N F E 🔥🔥🔥🔥

0개의 댓글