세미 조인은 보통 EXISTS를 사용하는 서브쿼리의 형태로 나타나며 이러한 경우 서브 쿼리에 인덱스가 존재하지 않는다면 상당히 비효율적인데 SEMI-JOIN이 일어나도록 유도한다면 성능의 향상을 꽤 할 수 있다. 즉 인덱스 없이 EXISTS를 사용하는 쿼리라면 HASH_SJ or MERGE_SJ or NL_SJ 힌트를 이용해서 세미조인이 일어나도록 푸는 것이 좋다.
2. Oracle Hint, Tuning_ 실행계획 SQL 연산
3.5 실행계획 SQL 연산
: HASH SEMI-JOIN
세미 조인은 보통 EXISTS를 사용하는 서브쿼리의 형태로 나타나며 이러한 경우
서브 쿼리에 인덱스가 존재하지 않는다면 상당히 비효율적인데 SEMI-JOIN이 일어나
도록 유도한다면 성능의 향상을 꽤 할 수 있다. 즉 인덱스 없이 EXISTS를 사용하는
쿼리라면 HASH_SJ or MERGE_SJ or NL_SJ 힌트를 이용해서 세미조인이 일어나도록
푸는 것이 좋다.
3. Oracle Hint, Tuning_ 실행계획 SQL 연산
-- RBO로 실행하면 FILTER 연산으로 비효율적인 실행계획이 만들어 진다. FILTER연산은 메인쿼리의 로우를 한건
읽을 때 마다 서브쿼리를 실행시키는 연산이다. RULE힌트의 경우 바깥쪽 메인쿼리의 MYEMP1 테이블을 FULL
TABLE SCAN 한다.
SQL> SELECT /*+ RULE */ COUNT(*)
FROM MYEMP1 E
WHERE EXISTS (
SELECT 1 FROM MYEMP1_OLD EO
WHERE E.EMPNO = EO.EMPNO
);
COUNT(*)
----------
1666667
경 과: 00:00:23.86
Execution Plan
---------------------------------------------------------------
| Id | Operation
---------------------------------------------------------------
| 0 | SELECT STATEMENT
| 1 | SORT AGGREGATE
|* 2 | FILTER
| 3 | TABLE ACCESS FULL | MYEMP1
|* 4 | INDEX UNIQUE SCAN | PK_MYEMP1_OLD
---------------------------------------------------------------
4. Oracle Hint, Tuning_ 실행계획 SQL 연산
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
10873520 consistent gets
-- 아래는 NO_UNNEST 힌트를 사용하여 CBO로 돌지만 서브쿼리를 조인으로 풀지말라고 했다. 바깥쪽 메인쿼리
의 MYEMP1 테이블의 PK_MYEMP1을 이용하여 INDEX FAST FULL SCAN 했다. 당연히 위에서 RULE 힌트를 사
용한 쿼리보다 읽어들인 블록수가 적으니 쿼리 수행시간도 좋다.
SQL> SELECT COUNT(*)
FROM MYEMP1 E
WHERE EXISTS (
SELECT /*+ no_unnest */ 1 FROM MYEMP1_OLD EO
WHERE E.EMPNO = EO.EMPNO
);
COUNT(*)
-------------
1666667
경 과: 00:00:4.05
5. Oracle Hint, Tuning_ 실행계획 SQL 연산
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 16M (1) | 55:40:12 |
| 1 | SORT AGGREGATE | | 1 | 13 | | |
|* 2 | FILTER | | | | | |
| 3 | INDEX FAST FULL SCAN| PK_MYEMP1 | 8488K| 105M| 5577 (1) | 00:01:07 |
|* 4 | INDEX UNIQUE SCAN | PK_MYEMP1_OLD | 1 | 6 | 2 (0 )| 00:00:01 |
----------------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
7 recursive calls
0 db block gets
10316854 consistent gets
-- 이번에는 HASH SEMI JOIN이 일어나도록 힌트를 사용해 보자. 물론 CBO 모드에서는 힌트를 사용하지 않더라
도 대부분 HASH SEMI JOIN을 이용하는 실행계획을 만들어 낼 것이다.
SQL> SELECT COUNT(*)
FROM MYEMP1 E
WHERE EXISTS (
SELECT /*+ HASH_SJ */ 1 FROM MYEMP1_OLD EO
WHERE E.EMPNO = EO.EMPNO
);
6. Oracle Hint, Tuning_ 실행계획 SQL 연산
COUNT(*)
------------
1666667
경 과: 00:00:07.17
Execution Plan
------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 12 | | 17257 (2)| 00:03:28 |
| 1 | SORT AGGREGATE | | 1 | 12 | | | |
|* 2 | HASH JOIN RIGHT SEMI | | 1666K| 19M| 28M| 17257 (2)| 00:03:28 |
| 3 | INDEX FAST FULL SCAN | PK_MYEMP1_OLD| 1666K| 9765K| | 1441 (2)| 00:00:18 |
| 4 | INDEX FAST FULL SCAN | PK_MYEMP1 | 10M| 57M| | 5726 (2)| 00:01:09 |
-----------------------------------------------------------------------------------------------------------------
7. Oracle Hint, Tuning_ 실행계획 SQL 연산
-- 이번에는 중첩 루프 세미조인(NESTED LOOP SEMI JOIN)을 이용하는 실행계획이 수립되도록 NL_SJ 힌트를
사용해 보자.
SQL> SELECT COUNT(*)
FROM MYEMP1 E
WHERE EXISTS (
SELECT /*+ NL_SJ */ 1 FROM MYEMP1_OLD EO
WHERE E.EMPNO = EO.EMPNO
);
COUNT(*)
----------
1666667
경 과: 00:00:06.07
Execution Plan
-------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 12 | 10M (1)| 33:22:25 |
| 1 | SORT AGGREGATE | | 1 | 12 | | |
| 2 | NESTED LOOPS SEMI | | 1666K| 19M| 10M (1)| 33:22:25 |
| 3 | INDEX FAST FULL SCAN| PK_MYEMP1 | 10M| 57M| 5726 (2)| 00:01:09 |
|* 4 | INDEX UNIQUE SCAN | PK_MYEMP1_OLD | 277K| 1627K| 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------