본문 바로가기
스터디/오라클 성능고도화 원리와 해법2

04 쿼리변환 - 서브쿼리 Unnesting

by 취미툰 2021. 8. 30.
반응형

쿼리변환이란?

결과가 동일하더라도 SQL문을 여떤 형태로 작성하느냐에 따라 성능에 차이가 날 수 있으며 쿼리 옵티마이저가 SQL을 분석해 의미적으로 동일하면서도 더 나은 성능이 개대되는 형태로 재작성하는 것을 말합니다.

옵티마이저의 서브엔진으로서 Query Transformer,Estimator,Plan Generator가 있는데 이중 Query Transformer가 그 역할을 담당합니다.

쿼리변환은 두가지 방식으로 작동합니다.

휴리스틱 쿼리변환 : 결과만 보장된다면 무조건 쿼리변환을 수행합니다. 일종의 규칙기반 최적화기법이라고 할 수 있습니다.

비용기반 쿼리변환 : 변환된 쿼리의 비용이 더 낮을 때만 그것을 사용하고, 그렇지 않을 때는 원본 쿼리 그대로 두고 최적화를 수행합니다.

 

서브쿼리 Unnesting

관련 힌트 unnest , no_unnest (서브쿼리쪽에서 힌트 사용)

중첩된 서브쿼리를 풀어내는 것을 말합니다. 반대의 의미는 No_Unnesting입니다.

인라인 뷰 : from절에 나타나는 서브쿼리

중첩된 서브쿼리 : where절에 사용된 서브쿼리. 특히 서브쿼리가 메인쿼리에 있는 컬럼을 참조하는 형태를 상관관계 있는 서브쿼리라고 부릅니다

스칼라 서브쿼리 : 한레코드당 정확히 하나의 컬럼 값만을 리턴하는 쿼리. select에 주로 사용되지만 몇 가지 예외사항을 뺀다면  컬럼이 올 수 있는 대부분의 위치에서 사용 가능합니다.

 

중첩된 서브쿼리는 메인쿼리와 부모와 자식이라는 종속적이고 계층적인 관계가 존재합니다. 따라서 논리적인 관점에서 처리과정은 In,Exists를 불문하고 필터방식이어야 합니다. 즉 메인쿼리에서 읽히는 레코드마다 서브쿼리를 반복수행하면서 조건에 맞지않는 데이터를 골라내는 것입니다.

하지만 필터방식이 항상 최적의 수행속도를 보장하지 못하므로 옵티마이저는 아래 둘중 하나를 선택합니다.

 

1.동일한 결과를 보장하는 조인문으로 변환하고 나서 최적화(서브쿼리 Unnesting)

2. 그대로 쿼리를 둔 상태에서 최적화

 

이점

서브쿼리를 같은 레벨로 풀어낸다면 다양한 액세스 경로와 조인 메소드를 평가할 수 있으며 옵티마이저는 많은 조인테크닉을 가지기 때문에 더나은 실행계획을 찾을 가능성이 높아집니다.

9i때에는 같은 결과집합임이 보장된다면 무조건 서브쿼리 Unnesting이 시도되었습니다.

10g부터는 비용기반 쿼리변환 방식으로 전환되어 예상비용이 더 낮을 때만 서브쿼리 Unnesting이 사용됩니다.

 

SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));

PLAN_TABLE_OUTPUT                                                                                                                                                                                                                                                                                           
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  3n801h1qfh2kk, child number 0                                                                                                                                                                                                                                                                       
-------------------------------------                                                                                                                                                                                                                                                                       
select * from emp where deptno in (select /*+ no_unnest */ deptno from                                                                                                                                                                                                                                      
dept)                                                                                                                                                                                                                                                                                                       
                                                                                                                                                                                                                                                                                                            
Plan hash value: 1499841400                                                                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                                                                                            
--------------------------------------------                                                                                                                                                                                                                                                                
| Id  | Operation          | Name | E-Rows |                                                                                                                                                                                                                                                                
--------------------------------------------                                                                                                                                                                                                                                                                
|   0 | SELECT STATEMENT   |      |        |                                                                                                                                                                                                                                                                
|*  1 |  FILTER            |      |        |                                                                                                                                                                                                                                                                
|   2 |   TABLE ACCESS FULL| EMP  |     14 |                                                                                                                                                                                                                                                                
|*  3 |   TABLE ACCESS FULL| DEPT |      1 |                                                                                                                                                                                                                                                                
--------------------------------------------                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                            
Predicate Information (identified by operation id):                                                                                                                                                                                                                                                         
---------------------------------------------------                                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                                                                            
1 - filter( IS NOT NULL)                                                                                                                                                                                                                                                                                    
3 - filter("DEPTNO"=:B1)

위의 실행계획을 확인해보면 메인쿼리가 서브쿼리가 별도의 최적화를 이루어 FILTER조건으로 마지막에 처리되는 것을 볼 수 있습니다. 즉 메인쿼리에서 읽히는 레코드마다 값을 넘기면서 서브쿼리를 반복 수행합니다.

 

Unnesting된 쿼리의 조인 순서조정

Unnesting에 의해 일반 조인문으로 변환된 후에는 어느 쪽이든 드라이빙 집합으로 선택될 수 있습니다.

 

Pushing 서브쿼리

Unnesting되지 않은 서브쿼리는 항상 필터방식으로 처리되며 대개 실행계획 상에서 맨 마지막 단계에 처리됩니다. 만약 서브쿼리 필터링을 먼저 처리했을 때 다음 수행단계로 넘어가는 로우 수를 크게 줄일 수 있다면 성능은 그만큼 향상됩니다. Pushing서브쿼리는 이처럼 실행계획상 가능한 앞단계에서 서브쿼리 필터링이 처리되도록 강제하는 것을 말하며 관련 힌트는 push_subq입니다. (인라인 뷰 안에 조건절을 밀어넣는 조건절 pusing과 헷갈리면 안됩니다!)

pushing서브쿼리는 항상 unnesting되지 않은 서브쿼리에만 작동하기 때문에 no_unnest힌트와 같이 사용됩니다.

 

반응형

댓글