서브쿼리 동작 방식을 제어하는 힌트들 HINT명설명
NO_UNNEST |
|
UNNEST |
|
NL_SJ |
|
HASH_SJ |
|
NL_AJ |
|
HASH_AJ |
|
ORDERED |
|
QB_NAME |
|
SWAP_JOIN_INPUTS |
|
NO_SWAP_JOIN_INPUTS |
|
PUSH_SUBQ |
|
--------------------------------------------------------------------------------------------
select ... from ...
where not exists (select /*+ UNNEST HASH_AJ parallel(4) */ 'x' from ... ) ;
--------------------------------------------------------------------------------------------
서브쿼리를 FILTER 동작 방식으로 수행하도록 제어
NO_UNNEST 힌트를 부여하면, Filter 동작 방식으로 수행하도록 제어할 수 있다.
SELECT C4, C5, C6 FROM SUBQUERY_T1 T1
WHERE C6 >= :B1
AND C6 <= :B2
AND EXISTS(SELECT /*+ NO_UNNEST*/ 'X' FROM SUBQUERY_T2 T2 WHERE T2.C1 = T1.C4 AND T2.C3 >= :B3 AND T2.C3 <= :B4);
NL SEMI JOIN으로 수행되도록 제어
NO_UNNEST 힌트를 부여하면, Filter 동작 방식으로 수행하도록 제어할 수 있다.
SELECT C4, C5, C6 FROM SUBQUERY_T1 T1
WHERE C6 >= :B1 AND C6 <= :B2
AND EXISTS(SELECT /*+ UNNEST NL_SJ*/ 'X' FROM SUBQUERY_T2 T2 WHERE T2.C1 = T1.C4 AND T2.C3 >= :B3 AND T2.C3 <= :B4);
HASH JOIN SEMI JOIN으로 수행되며, 서브쿼리를 Main SQL 테이블 보다 먼저 수행하도록 제어
UNNEST와 HASH_SJ, SWAP_JOIN_INPUTS힌트를 사용하면 서브쿼리부터 수행하도록 실행계획 제어 가능
SELECT C4, C5, C6 FROM SUBQUERY_T1 T1
WHERE C6 >= :B1 AND C6 <= :B2
AND EXISTS(SELECT /*+ UNNEST HASH_SJ SWAP_JOIN_INPUTS(T2)*/ 'X' FROM SUBQUERY_T2 T2 WHERE T2.C1 = T1.C4 AND T2.C3 >= :B3 AND T2.C3 <= :B4);
SQL 서브쿼리를 HASH SEMI JOIN으로 수행하되, Main SQL 테이블을 먼저 수행하도록 제어
UNNEST와 HASH_SJ 힌트를 사용하면, HASH SEMI JOIN으로 수행하도록 제어 SEMI JOIN은 MAIN SQL쪽 테이블을 먼저 수행하는 것이 기본이나 HASH RIGHT SEMI JOIN으로 수행되면 조인 순서가 변경되므로 NO_SWAP_JOIN_INPUTS 힌트를 명시적으로 사용
SELECT C4, C5, C6 FROM SUBQUERY_T1 T1
WHERE C6 >= :B1 AND C6 <= :B2
AND EXISTS(SELECT /*+ UNNEST HASH_SJ NO_SWAP_JOIN_INPUTS(T2) */ 'X' FROM SUBQUERY_T2 T2 WHERE T2.C1 = T1.C4 AND T2.C3 >= :B3 AND T2.C3 <= :B4);
SQL을 NL JOIN으로 수행하되, 서브쿼리를 수행하도록 제어
QB_NAME 힌트를 사용해 QUERY BLOCK명을 지정한 후, QUERY BLOCK 명을 지정한 후, 조인 순서와 조인 방법을 제어
SELECT /*+ QB_NAME(MAIN) LEADING(T2@SUB) USE_NAME(T1@MAIN) */ C4, C5, C6 FROM SUBQUERY_T1 T1
WHERE C6 >= :B1 AND C6 <= :B2AND EXISTS(SELECT /*+ UNNEST QB_NAME(SUB) */ 'X' FROM SUBQUERY_T2 T2 WHERE T2.C1 = T1.C4 AND T2.C3 >= :B3 AND T2.C3 <= :B4);
HASH JOIN으로 처리하되, 허브쿼리로부터 수행하도록 제어
SELECT /*+ QB_NAME(MAIN) LEADING(T2@SUB) USE_HASH(T1@MAIN) */ C4, C5, C6 FROM SUBQUERY_T1 T1
WHERE C6 >= :B1 AND C6 <= :B2 AND EXISTS(SELECT /*+ UNNEST QB_NAME(SUB) */ 'X' FROM SUBQUERY_T2 T2 WHERE T2.C1 = T1.C4 AND T2.C3 >= :B3 AND T2.C3 <= :B4);
NOT EXISTS로 작성된 SQL을 NL JOIN ANTI로 수행하도록 제어
NOT EXISTS의 경우 NL JOIN ANTI로 수행 제어 하기 위해서 UNNEST, NL_AJ힌트를 부여
SELECT C4, C5, C6 FROM SUBQUERY_T1 T1 WHERE C6 >= :B1 AND C6 <= :B2 AND EXISTS(SELECT /*+ UNNEST NL_AJ */ 'X' FROM SUBQUERY_T2 T2 WHERE T2.C1 = T1.C4 AND T2.C3 >= :B3 AND T2.C3 <= :B4);
NOT EXISTS로 작성된 SQL을 HASH JOIN ANTI 조인으로 수행하도록 제어
SELECT C4, C5, C6 FROM SUBQUERY_T1 T1
WHERE C6 >= :B1 AND C6 <= :B2 AND EXISTS(SELECT /*+ UNNEST HASH_AJ */ 'X' FROM SUBQUERY_T2 T2 WHERE T2.C1 = T1.C4 AND T2.C3 >= :B3 AND T2.C3 <= :B4);
서브쿼리를 먼저 읽은 후, NL 으로 수행
SELECT * FROM EMP E WHERE EMPNO IN (SELECT MAX(EMPNO) FROM EMP X GROUP BY DEPTNO);
-- 서브쿼리를 먼저 읽은 후, NL로 수행
SELECT /*+ LEADING(X@SUB) QB_NAME(MAIN) USE_NL(E@MAIN) */ * FROM EMP E
WHERE EMPNO IN (SELECT /*+ UNNEST QB_NAME(SUB) */ MAX(EMPNO) FROM EMP X GROUP BY DEPTNO);
- 의도한대로 제어되지 않는 이유는 OPTIMIZER가 서브쿼리를 인라인 뷰로 변경하는 SQL 최적화 작업을 수행했기 때문이다.
- VW_NSO_1이란 점에서 추축 가능
- SQL이 변경되고, 이론 인해 QUERY BLOCK명도 변경되어 QB_NAME 힌트는 물론, 다른 힌트들도 무시
- 이런 경우는 FROM절에 나열된 순서대로 조인 순서를 결정하는 ORDERED 힌트를 사용하면 유도 할 수 있음
- LOGICAL OPTIMIZER가 서브쿼리를 인라인 뷰로 변경할 때 FROM절의 맨 앞에 위치 시키기 대문에 ORDERED로 유도 가능
SELECT /*+ ORDERED USE_NL(E) */ * FROM EMP E
WHERE EMPNO IN (SELECT /*+ UNNEST */ MAX(EMPNO) FROM EMP X GROUP BY DEPTNO);
- SUBQUERY가 여러개 일 경우 ORDERED 힌트로 제어 불가.
출처: https://argolee.tistory.com/89 [놀멍:티스토리]
'DBA' 카테고리의 다른 글
오라클 튜닝 실습 (1) | 2024.10.13 |
---|---|
접근 방법을 결정하는 힌트절? USE_NL, USE_HASH (0) | 2024.09.13 |
MySQL8 - Semi Join 과 Anti Join - 안티조인 세미조인 (1) | 2024.09.12 |
DBMS_XPLAN.DISPLAY_CURSOR 결과 보는 법 (0) | 2024.09.12 |
접근 순서를 결정하는 힌트절 ? ORDERED, LEADING (0) | 2024.09.12 |
댓글