일반적인 서브쿼리란? |
|
-- 사용패턴 1
-- 서브쿼리 추출 결과는 반드시 1건 (서브쿼리 -> Main SQL)
SELECT *
FROM emp
WHERE sal > (SELECT avg(sal)
FROM emp)
-- 사용패턴 2
-- 서브쿼리 추출 결과가 여러 건 (서브쿼리 <-> Main SQL)
-- 성능문제가 주로 발생하는 패턴
SELECT c1,
c2,
c3
FROM SUBQUERY_T2 t2
WHERE c2 = 'A'
AND EXISTS (SELECT /*+ NO_UNNEST */'x'
FROM SUBQUERY_T1 t1
WHERE t1.c5 = t2.c2)
인라인 뷰(Inline View) | from 절에 나타나는 서브쿼리 |
중첩된 서브쿼리(Nested Subquery) | where 절에 사용된 서브쿼리. 특히, 서브쿼리가 메인쿼리에 있는 컬럼을 참조하는 형태는 '상관관계 있는(Correlated) 서브쿼리'라고 함. |
스칼라 서브쿼리(Scalar Subquery) | 한 레코드당 하나의 컬럼 값만을 리턴하는 서브 쿼리. |
-- Main SQL 추출 건수 : 380,001건
-- t2.c1은 unique한 컬럼
SELECT c1,
c2,
c3
FROM SUBQUERY_T2 t2
WHERE c1 >= :b1 AND c1 <= :b2
AND EXISTS (SELECT /*+ NO_UNNEST */'x'
FROM SUBQUERY_T1 t1
WHERE t1.c4 = t2.c1)
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.01 0 2 0 0
Execute 1 0.01 0.00 0 0 0 0
Fetch 15535 2.34 2.41 0 1156653 0 230001
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 15337 2.35 2.42 0 1156653 0 230001
Rows Row Source Operation
------- ---------------------------------------------------
230001 FILTER (cr=1156653 pr=0 pw=0 time=1846157 us)
380001 FILTER (cr=16650 pr=0 pw=0 time=3336 us)
380001 TABLE ACCESS SUBQUERY_T2 (cr=16650 pr=0 pw=0 time=35462 us)
230001 INDEX RANGE SCAN SUBQUERY_T1_IDX_01 (cr=1140003 pr=0 ...)
-- Main SQL 추출 건수 : 5건
-- t2.c1은 unique한 컬럼
SELECT c1,
c2,
c3
FROM SUBQUERY_T2 t2
WHERE c1 >= :b1 AND c1 <= :b2
AND EXISTS (SELECT /*+ NO_UNNEST */'x'
FROM SUBQUERY_T1 t1
WHERE t1.c4 = t2.c1)
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.01 0.01 0 1332 0 5
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.01 0.01 0 1332 0 5
Rows Row Source Operation
------- ---------------------------------------------------
230001 FILTER (cr=1332 pr=0 pw=0 time=385929 us)
380001 FILTER (cr=26650 pr=0 pw=0 time=3480 us)
380001 TABLE ACCESS SUBQUERY_T2 (cr=26650 pr=0 pw=0 time=40002 us)
230001 INDEX RANGE SCAN SUBQUERY_T1_IDX_01 (cr=15 pr=0 pw=0 time=223 us)
-- Main SQL 추출 건수 : 380,001건
-- t2.c2는 값의 종류가 26가지
SELECT c1,
c2,
c3
FROM SUBQUERY_T2 t2
WHERE c1 >= :b1 AND c1 <= :b2
AND EXISTS (SELECT /*+ NO_UNNEST */'x'
FROM SUBQUERY_T1 t1
WHERE t1.c5 = t2.c2)
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 25335 1.01 1.25 0 26728 0 380001
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 25337 1.01 1.25 0 26728 0 380001
Rows Row Source Operation
------- ---------------------------------------------------
380001 FILTER (cr=26728 pr=0 pw=0 time=385929 us)
380001 FILTER (cr=26650 pr=0 pw=0 time=3480 us)
380001 TABLE ACCESS SUBQUERY_T2 (cr=26650 pr=0 pw=0 time=40002 us)
26 INDEX RANGE SCAN SUBQUERY_T1_IDX_02 (cr=78 pr=0 pw=0 time=223 us)
구분 | 내용 |
---|---|
수행 순서 | Main SQL이 먼저 수행된다. |
Main SQL 추출건수 | 최대 Main SQL 추출 건수 만큼 서브 쿼리가 수행된다. Main SQL 추출 건수가 적을 경우에는 Filter 동작방식은 불리하지 않다. |
Input 값의 종류 | Unique 할 경우 Main SQL 추출 건수 만큼 서브쿼리가 수행된다. 값의 종류가 적을 경우, 최소 값의 종류만큼만 서브쿼리가 수행된다. |
유연성 | Main SQL을 먼저 수행해야만 하므로 다양한 상황에서 유연하게 대처하기는 어려운 동작 방식. |
SELECT c1,
c2,
c3
FROM SUBQUERY_T2 t2
WHERE c1 >= :b1 AND c1 <= :b2
AND EXISTS (SELECT /*+ NO_UNNEST */'x'
FROM SUBQUERY_T1 t1
WHERE t1.c6 = t2.c3 AND t1.c6 >= :b1)
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.01 0.01 0 0 0 0
Execute 1 0.01 0.00 0 0 0 0
Fetch 2 156.11 157.03 0 7863857 0 11
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 156.12 157.03 0 7863857 0 11
Rows Row Source Operation
------- ---------------------------------------------------
11 FILTER (cr=7863857 pr=0 pw=0 time=157033184 us)
221 FILTER (cr=7 pr=0 pw=0 time=2467 us)
221 TABLE ACCESS BY INDEX ROWIDSUBQUERY_T2 (cr=7 pr=0 pw=0 time=2125 us)
221 INDEX RANGE SCAN PK_SUBQUERY_2 (cr=4 pr=0 pw=0 time=690 us)
11 FILTER (cr=7863850 pr=0 pw=0 time=157028384 us)
11 TABLE ACCESS FULL SUBQUERY_T1 (cr=7863850 pr=0 pw=0 time=157028031 us)
SELECT c1,
c2,
c3
FROM SUBQUERY_T2 t2
WHERE c1 >= :b1 AND c1 <= :b2
AND EXISTS (SELECT /*+ UNNEST HASH_SJ */'x'
FROM SUBQUERY_T1 t1
WHERE t1.c6 = t2.c3 AND t1.c6 >= :b1)
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.01 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 1.90 1.91 0 37422 0 11
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 1.90 1.92 0 37422 0 11
Rows Row Source Operation
------- ---------------------------------------------------
11 FILTER (cr=37422 pr=0 pw=0 time=1910470 us)
221 HASH JOIN SEMI (cr=37422 pr=0 pw=0 time=1910466 us)
221 TABLE ACCESS BY INDEX ROWIDSUBQUERY_T2 (cr=5 pr=0 pw=0 time=42 us)
221 INDEX RANGE SCAN PK_SUBQUERY_2 (cr=3 pr=0 pw=0 time=31 us)
6400640 TABLE ACCESS FULL SUBQUERY_T1 (cr=37417 pr=0 pw=0 time=6261 us)
HINT 명 | 설명 |
---|---|
NO_UNNEST | 서브쿼리를 Filter 방식으로 유도하는 HINT |
UNNEST | 서브쿼리를 조인동작방식으로 유도하는 HINT |
NL_SJ | 조인동작방식 중 Nested Loops Join Semi로 유도하는 HINT |
HASH_SJ | 조인동작방식 중 Hash Join Semi로 유도하는 HINT |
NL_AJ | 조인동작방식 중 Nested Loops Join Anti로 유도하는 HINT |
HASH_AJ | 조인동작방식 중 Hash Join Anti로 유도하는 HINT |
ORDERED | FROM절에 나열된 순서대로 조인 순서를 정하는 HINT (SUB QUERY가 존재하면 서브쿼리부터 수행하도록 유도함 |
QB_NAME | QUERY BLOCK의 이름을 지정하는 HINT |
SWAP_JOIN_INPUTS | HASH OUTER JOIN과 같이 순서가 고정된 상황에서 조인 순서를 바꾸도록 유도하는 HINT |
NO_SWAP_JOIN_INPUTS | HASH OUTER JOIN과 같이 순서가 고정된 상황에서 조인 순서를 바꾸지 못하도록 유도하는 HINT |
PUSH_SUBQ | 서브쿼리가 먼저 수행하도록 유도하는 HINT |
원본 쿼리 1
SQL> select * from dept d
2 where dname = 'SALES'
3 and exists (
4 select 'x'
5 from emp
6 where deptno = d.deptno
7 and job = 'SALESMAN'
8 and sal > 1000)
9 ;
Execution Plan
----------------------------------------------------------
Plan hash value: 3497946635
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 62 | 3 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 1 | 62 | 3 (0)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 30 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | DEPT_X01 | 1 | | 1 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| EMP | 4 | 128 | 1 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | EMP_X01 | 2 | | 0 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("DNAME"='SALES')
4 - filter("SAL">1000)
5 - access("DEPTNO"="D"."DEPTNO" AND "JOB"='SALESMAN')
1번
1 select * from dept d
2 where dname = 'SALES'
3 and exists (
4 select /*+ no_unnest no_push_subq */ 'x'
5 from emp
6 where deptno = d.deptno
7 and job = 'SALESMAN'
8* and sal > 1000)
9 ;
Execution Plan
----------------------------------------------------------
Plan hash value: 3269917256
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 4 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 30 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | DEPT_X01 | 1 | | 1 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 32 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | EMP_X01 | 2 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( EXISTS (SELECT /*+ NO_PUSH_SUBQ NO_UNNEST */ 0 FROM "EMP" "EMP"
WHERE "JOB"='SALESMAN' AND "DEPTNO"=:B1 AND "SAL">1000))
3 - access("DNAME"='SALES')
4 - filter("SAL">1000)
5 - access("DEPTNO"=:B1 AND "JOB"='SALESMAN')
2번
1 select * from dept d
2 where dname = 'SALES'
3 and exists (
4 select /*+ no_unnest no_push_subq */ 'x'
5 from emp
6 where deptno = d.deptno
7* and job = 'SALESMAN')
8 ;
Execution Plan
----------------------------------------------------------
Plan hash value: 2128684681
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 3 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 30 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | DEPT_X01 | 1 | | 1 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | EMP_X01 | 1 | 19 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( EXISTS (SELECT /*+ NO_PUSH_SUBQ NO_UNNEST */ 0 FROM "EMP" "EMP"
WHERE "JOB"='SALESMAN' AND "DEPTNO"=:B1))
3 - access("DNAME"='SALES')
4 - access("DEPTNO"=:B1 AND "JOB"='SALESMAN')
3번
1 select * from dept d
2 where dname = 'SALES'
3 and exists (
4 select /*+ no_unnest */ 'x'
5 from emp
6 where deptno = d.deptno
7 and job = 'SALESMAN'
8* and sal >= 1000)
SQL>
SQL> /
Execution Plan
----------------------------------------------------------
Plan hash value: 3410011884
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 4 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | DEPT | 1 | 30 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | DEPT_X01 | 1 | | 1 (0)| 00:00:01 |
|* 3 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 32 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | EMP_X01 | 2 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DNAME"='SALES')
filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "EMP" "EMP" WHERE
"JOB"='SALESMAN' AND "DEPTNO"=:B1 AND "SAL">=1000))
3 - filter("SAL">=1000)
4 - access("DEPTNO"=:B1 AND "JOB"='SALESMAN')
4번
1 select /*+ index(d dept_x01) */ * from dept d
2 where dname = 'SALES'
3 and exists (
4 select /*+ no_unnest index(emp emp_x01) */ 'x'
5 from emp
6 where deptno = d.deptno
7* and job = 'SALESMAN')
SQL> /
Execution Plan
----------------------------------------------------------
Plan hash value: 166048891
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 30 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | DEPT_X01 | 1 | | 1 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | EMP_X01 | 1 | 19 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DNAME"='SALES')
filter( EXISTS (SELECT /*+ NO_UNNEST INDEX ("EMP" "EMP_X01") */ 0 FROM
"EMP" "EMP" WHERE "JOB"='SALESMAN' AND "DEPTNO"=:B1))
3 - access("DEPTNO"=:B1 AND "JOB"='SALESMAN')
5번
drop index dept_x01;
create index dept_x01 on dept(dname);
SQL> select /*+ index(d dept_x01) */ * from dept d
2 where dname = 'SALES'
3 and exists (
4 select /*+ no_unnest push_subq */ 'x'
5 from emp
6 where deptno = d.deptno
7 and job = 'SALESMAN'
8 and sal > 1000);
Execution Plan
----------------------------------------------------------
Plan hash value: 3234211606
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 4 (0)| 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID | DEPT | 1 | 30 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | DEPT_X01 | 1 | | 1 (0)| 00:00:01 |
|* 3 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 32 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | EMP_X01 | 2 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( EXISTS (SELECT /*+ PUSH_SUBQ NO_UNNEST */ 0 FROM "EMP" "EMP"
WHERE "JOB"='SALESMAN' AND "DEPTNO"=:B1 AND "SAL">1000))
2 - access("DNAME"='SALES')
3 - filter("SAL">1000)
4 - access("DEPTNO"=:B1 AND "JOB"='SALESMAN')
6번
1 select /*+ index(d dept_x01) */ * from dept d
2 where dname = 'SALES'
3 and exists (
4 select /*+ no_unnest push_subq */ 'x'
5 from emp
6 where deptno = d.deptno
7* and job = 'SALESMAN')
SQL> /
Execution Plan
----------------------------------------------------------
Plan hash value: 3152071781
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 30 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | DEPT_X01 | 1 | | 1 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | EMP_X01 | 1 | 19 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( EXISTS (SELECT /*+ PUSH_SUBQ NO_UNNEST */ 0 FROM "EMP" "EMP"
WHERE "JOB"='SALESMAN' AND "DEPTNO"=:B1))
2 - access("DNAME"='SALES')
3 - access("DEPTNO"=:B1 AND "JOB"='SALESMAN')
7번
drop index emp_x01 ;
create index emp_x01 on emp(sal, deptno);
SQL> select * from dept d
2 where deptno = (
3 select /*+ index(e emp_x01) */ deptno
4 from emp e
5 where sal >= 5000
6 and job = 'PRESIDENT')
7 ;
Execution Plan
----------------------------------------------------------
Plan hash value: 2576796375
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | DEPT | 1 | 30 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | DEPT_PK | 1 | | 0 (0)| 00:00:01 |
|* 3 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 32 | 2 (0)| 00:00:01 |
|* 4 | INDEX FULL SCAN | EMP_X01 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DEPTNO"= (SELECT /*+ INDEX ("E" "EMP_X01") */ "DEPTNO" FROM "EMP"
"E" WHERE "SAL">=5000 AND "JOB"='PRESIDENT'))
3 - filter("SAL">=5000)
4 - access("JOB"='PRESIDENT')
filter("JOB"='PRESIDENT')
8번
1 select * from dept d
2 where deptno = (
3 select /*+ index(e emp_x01) */ deptno
4 from emp e
5 where 1 = 1
6* and job = 'PRESIDENT')
7 /
Execution Plan
----------------------------------------------------------
Plan hash value: 3718500214
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 30 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | DEPT_PK | 1 | | 0 (0)| 00:00:01 |
|* 3 | INDEX FULL SCAN | EMP_X01 | 1 | 19 | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DEPTNO"= (SELECT /*+ INDEX ("E" "EMP_X01") */ "DEPTNO" FROM
"EMP" "E" WHERE "JOB"='PRESIDENT'))
3 - access("JOB"='PRESIDENT')
filter("JOB"='PRESIDENT')
9번
1 select * from dept d
2 where deptno = (
3 select /*+ index(e emp_x01) */ max(deptno)
4 from emp e
5 where 1 = 1
6 and sal > 5000
7* and job = 'PRESIDENT')
SQL> /
Execution Plan
----------------------------------------------------------
Plan hash value: 211460956
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | DEPT | 1 | 30 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | DEPT_PK | 1 | | 0 (0)| 00:00:01 |
| 3 | SORT AGGREGATE | | 1 | 32 | | |
|* 4 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 32 | 2 (0)| 00:00:01 |
|* 5 | INDEX FULL SCAN | EMP_X01 | 1 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DEPTNO"= (SELECT /*+ INDEX ("E" "EMP_X01") */ MAX("DEPTNO") FROM
"EMP" "E" WHERE "SAL">5000 AND "JOB"='PRESIDENT'))
4 - filter("SAL">5000)
5 - access("JOB"='PRESIDENT')
filter("JOB"='PRESIDENT')
10번
1 select * from dept d
2 where deptno = (
3 select /*+ index(e emp_x01) */ max(deptno)
4 from emp e
5 where 1 = 1
6* and job = 'PRESIDENT')
SQL> /
Execution Plan
----------------------------------------------------------
Plan hash value: 3985888286
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | DEPT | 1 | 30 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | DEPT_PK | 1 | | 0 (0)| 00:00:01 |
| 3 | SORT AGGREGATE | | 1 | 19 | | |
| 4 | FIRST ROW | | 1 | 19 | 1 (0)| 00:00:01 |
|* 5 | INDEX FULL SCAN (MIN/MAX)| EMP_X01 | 1 | 19 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DEPTNO"= (SELECT /*+ INDEX ("E" "EMP_X01") */ MAX("DEPTNO") FROM
"EMP" "E" WHERE "JOB"='PRESIDENT'))
5 - filter("JOB"='PRESIDENT')
상황1 아래 SQL에서 서브쿼리를 먼저 읽은 후, Nested Loops Join 으로 수행하도록 해보세요.
쿼리
SQL> select *
2 from emp a
3 where empno in (
4 select max(empno)
5 from emp x
6 group by deptno);
Execution Plan
----------------------------------------------------------
Plan hash value: 3051006254
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 1400 | 7 (29)| 00:00:01 |
| 1 | MERGE JOIN | | 14 | 1400 | 7 (29)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| EMP | 14 | 1218 | 2 (0)| 00:00:01 |
| 3 | INDEX FULL SCAN | EMP_PK | 14 | | 1 (0)| 00:00:01 |
|* 4 | SORT JOIN | | 14 | 182 | 5 (40)| 00:00:01 |
| 5 | VIEW | VW_NSO_1 | 14 | 182 | 4 (25)| 00:00:01 |
| 6 | HASH GROUP BY | | 14 | 364 | 4 (25)| 00:00:01 |
| 7 | TABLE ACCESS FULL | EMP | 14 | 364 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("EMPNO"="MAX(EMPNO)")
filter("EMPNO"="MAX(EMPNO)")
시도1
1 select /* use_nl(x) */ *
2 from emp a
3 where empno in (
4 select /*+ unnest push_subq */ max(empno)
5 from emp x
6* group by deptno)
SQL> /
Execution Plan
----------------------------------------------------------
Plan hash value: 3051006254
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 153 | 7 (29)| 00:00:01 |
| 1 | MERGE JOIN | | 3 | 153 | 7 (29)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| EMP | 14 | 532 | 2 (0)| 00:00:01 |
| 3 | INDEX FULL SCAN | EMP_PK | 14 | | 1 (0)| 00:00:01 |
|* 4 | SORT JOIN | | 3 | 39 | 5 (40)| 00:00:01 |
| 5 | VIEW | VW_NSO_1 | 3 | 39 | 4 (25)| 00:00:01 |
| 6 | HASH GROUP BY | | 3 | 21 | 4 (25)| 00:00:01 |
| 7 | TABLE ACCESS FULL | EMP | 14 | 98 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("EMPNO"="MAX(EMPNO)")
filter("EMPNO"="MAX(EMPNO)")
시도2
1 select /* leading(x@sub) qb_name(main) use_nl(a@main) */ *
2 from emp a
3 where empno in (
4 select /*+ unnest push_subq qb_name(sub) */ max(empno)
5 from emp x
6* group by deptno)
SQL> /
Execution Plan
----------------------------------------------------------
Plan hash value: 3051006254
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 153 | 7 (29)| 00:00:01 |
| 1 | MERGE JOIN | | 3 | 153 | 7 (29)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| EMP | 14 | 532 | 2 (0)| 00:00:01 |
| 3 | INDEX FULL SCAN | EMP_PK | 14 | | 1 (0)| 00:00:01 |
|* 4 | SORT JOIN | | 3 | 39 | 5 (40)| 00:00:01 |
| 5 | VIEW | VW_NSO_1 | 3 | 39 | 4 (25)| 00:00:01 |
| 6 | HASH GROUP BY | | 3 | 21 | 4 (25)| 00:00:01 |
| 7 | TABLE ACCESS FULL | EMP | 14 | 98 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("EMPNO"="MAX(EMPNO)")
filter("EMPNO"="MAX(EMPNO)")
최종 시도
1 select /*+ ordered use_nl(a) */ *
2 from emp a
3 where empno in (
4 select /*+ unnest */ max(empno)
5 from emp x
6* group by deptno)
SQL> /
Execution Plan
----------------------------------------------------------
Plan hash value: 2044576694
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 153 | 7 (15)| 00:00:01 |
| 1 | NESTED LOOPS | | | | | |
| 2 | NESTED LOOPS | | 3 | 153 | 7 (15)| 00:00:01 |
| 3 | VIEW | VW_NSO_1 | 3 | 39 | 4 (25)| 00:00:01 |
| 4 | HASH GROUP BY | | 3 | 21 | 4 (25)| 00:00:01 |
| 5 | TABLE ACCESS FULL | EMP | 14 | 98 | 3 (0)| 00:00:01 |
|* 6 | INDEX UNIQUE SCAN | EMP_PK | 1 | | 0 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 38 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - access("EMPNO"="MAX(EMPNO)")