인라인 뷰(Inline View) | from 절에 나타나는 서브쿼리 |
중첩된 서브쿼리(Nested Subquery) | where 절에 사용된 서브쿼리. 특히, 서브쿼리가 메인쿼리에 있는 컬럼을 참조하는 형태는 '상관관계 있는(Correlated) 서브쿼리'라고 함. |
스칼라 서브쿼리(Scalar Subquery) | 한 레코드당 하나의 컬럼 값만을 리턴하는 서브 쿼리. |
-- 원래 쿼리
select * from emp
where deptno in (select deptno from dept)
;
-- no_unnest
SQL> explain plan for
2 select * from emp
3 where deptno in (select /*+ no_unnest */ deptno from dept)
4 ;
해석되었습니다.
경 과: 00:00:00.14
SQL> @plan
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1783302997
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 185 | 3 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS FULL| EMP | 14 | 518 | 3 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN| PK_DEPT | 1 | 3 | 0 (0)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "DEPT" "DEPT"
WHERE "DEPTNO"=:B1))
3 - access("DEPTNO"=:B1)
21 개의 행이 선택되었습니다.
경 과: 00:00:00.11
-- unnest
SQL> explain plan for
2 select * from emp
3 where deptno in (select /*+ unnest */ deptno from dept)
4 ;
해석되었습니다.
경 과: 00:00:00.03
SQL> @plan
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------
Plan hash value: 3074306753
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 560 | 3 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 14 | 560 | 3 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| EMP | 14 | 518 | 3 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN| PK_DEPT | 1 | 3 | 0 (0)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("DEPTNO"="DEPTNO")
19 개의 행이 선택되었습니다.
경 과: 00:00:00.03
select /*+ leading(dept) */ * from emp
where deptno in (select /*+ unnest */ deptno from dept)
select /*+ ordered */ * from emp
where deptno in (select /*+ unnest */ deptno from dept)
select /*+ leading(dept@qb1) */ * from emp
where deptno in (select /*+ unnest qb_name(qb1) */ deptno from dept)
select * from dept
where deptno in (select deptno from emp)
select *
from (select deptno from emp) a, dept b
where b.deptno = a.deptno
select * from emp
where deptno in (select deptno from dept)
alter table dept drop primary key;
create index dept_deptno_idx on dept(deptno);
SQL> explain plan for
2 select /*+ leading(dept@qb1) use_nl(emp) */ * from emp
3 where deptno in (select /*+ unnest qb_name(qb1) */ deptno from dept)
4 ;
해석되었습니다.
경 과: 00:00:00.00
SQL> @plan
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
Plan hash value: 2059974117
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 560 | 6 (17)| 00:00:01 |
| 1 | NESTED LOOPS | | 14 | 560 | 6 (17)| 00:00:01 |
| 2 | SORT UNIQUE | | 4 | 12 | 1 (0)| 00:00:01 |
| 3 | INDEX FULL SCAN | DEPT_DEPTNO_IDX | 4 | 12 | 1 (0)| 00:00:01 |
|* 4 | TABLE ACCESS FULL| EMP | 4 | 148 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("DEPTNO"="DEPTNO")
16 개의 행이 선택되었습니다.
select b.*
from (select /*+ no_merge */ distinct deptno from dept order by deptno) a, emp b
where b.deptno = a.deptno
SQL> explain plan for
2 select * from emp
3 where deptno in (select deptno from dept)
4 ;
해석되었습니다.
경 과: 00:00:00.00
SQL> @plan
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
Plan hash value: 3740080709
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 560 | 3 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 14 | 560 | 3 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| EMP | 14 | 518 | 3 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | DEPT_DEPTNO_IDX | 4 | 12 | 0 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("DEPTNO"="DEPTNO")
15 개의 행이 선택되었습니다.
-- emp 테이블을 100번 복제한 t_emp 테이블 생성
create table t_emp
as
select *
from emp
,(select rownum no from dual connect by level <= 100)
;
-- 필터 방식으로 수행
select count(*)
from t_emp t
where exists (select /*+ no_unnest */
'x'
from dept
where deptno = t.deptno
and loc is not null)
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.01 0 2 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.00 0.00 0 18 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.00 0.01 0 20 0 1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 91
Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE (cr=18 pr=0 pw=0 time=0 us)
1400 FILTER (cr=18 pr=0 pw=0 time=18 us)
1400 TABLE ACCESS FULL T_EMP (cr=12 pr=0 pw=0 time=4 us cost=5 size=18200 card=1400)
3 TABLE ACCESS BY INDEX ROWID DEPT (cr=6 pr=0 pw=0 time=0 us cost=2 size=11 card=1)
3 INDEX RANGE SCAN DEPT_DEPTNO_IDX (cr=3 pr=0 pw=0 time=0 us cost=1 size=0 card=1)(object id 91722)
1400 NESTED LOOPS SEMI (cr=1414 r=O w=O time=46453 us)
1400 TABLE ACCESS FULL T_EMP (cr=12 r=O w=O time=6613 us)
1400 TABLE ACCESS BY INDEX ROWID DEPT (cr=1402 r=O w=O time=20490 us)
1400 INDEX UNIQUE SCAN DETP_PK (cr=2 r=O w=O timee=6948 us) (object id 39130)
select count(*)
from t_emp t
where exists (select /*+ unnest nl_sj */
'x'
from dept
where deptno = t.deptno
and loc is not null)
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 1 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.00 0.00 0 15 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.00 0.00 0 16 0 1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 91
Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE (cr=15 pr=0 pw=0 time=0 us)
1400 NESTED LOOPS SEMI (cr=15 pr=0 pw=0 time=18 us cost=1406 size=33600 card=1400)
1400 TABLE ACCESS FULL T_EMP (cr=12 pr=0 pw=0 time=6 us cost=5 size=18200 card=1400)
3 TABLE ACCESS BY INDEX ROWID DEPT (cr=3 pr=0 pw=0 time=0 us cost=1 size=44 card=4)
3 INDEX RANGE SCAN DEPT_DEPTNO_IDX (cr=2 pr=0 pw=0 time=0 us cost=0 size=0 card=1)(object id 91722)
-- Unnesting 하지 않아서 필터 처리됨
SQL> explain plan for
2 select *
3 from dept d
4 where not exists(select /*+ no_unnest */
5 'x'
6 from emp
7 where deptno = d.deptno)
8 ;
해석되었습니다.
경 과: 00:00:00.07
SQL> @plan
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------
Plan hash value: 3547749009
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 60 | 7 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS FULL| DEPT | 4 | 80 | 3 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| EMP | 2 | 6 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( NOT EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "EMP" "EMP"
WHERE "DEPTNO"=:B1))
3 - filter("DEPTNO"=:B1)
17 개의 행이 선택되었습니다.
-- Unnesting 하면 Anti 조인 방식으로 처리됨.
SQL> explain plan for
2 select *
3 from dept d
4 where not exists(select /*+ unnest nl_aj */
5 'x'
6 from emp
7 where deptno = d.deptno)
8 ;
해석되었습니다.
경 과: 00:00:00.00
SQL> @plan
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------
Plan hash value: 1522491139
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 69 | 10 (0)| 00:00:01 |
| 1 | NESTED LOOPS ANTI | | 3 | 69 | 10 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| DEPT | 4 | 80 | 3 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| EMP | 5 | 15 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("DEPTNO"="D"."DEPTNO")
15 개의 행이 선택되었습니다.
-- merge_aj
select *
from dept d
where not exists(select /*+ unnest merge_aj */
'x'
from emp
where deptno = d.deptno)
;
-- hash_aj
select *
from dept d
where not exists(select /*+ unnest hash_aj */
'x'
from emp
where deptno = d.deptno)
;
-- 집계 서브쿼리가 있는 쿼리
select d.deptno, d.dname, e.empno, e.ename, e.sal
from emp e, dept d
where d.deptno = e.deptno
and e.sal >= (select avg(sal) from emp where deptno = d.deptno)
-- 분석함수를 사용하는 형태로 변환된 쿼리
select deptno, dname, empno, ename, sal
from (select d.deptno, d.dname, e.empno, e.ename, e.sal
,(case when e.sal >= avg(sal) over (partition by d.deptno)
then e.rowid end) max_sal_rowid
from emp e, dept d
where d.deptno = e.deptno)
where max_sal_rowid is not null
-- 분석함수를 사용하는 형태로 변환되었을 때 실행계획
SQL> explain plan for
2 select d.deptno, d.dname, e.empno, e.ename, e.sal
3 from emp e, dept d
4 where d.deptno = e.deptno
5 and e.sal >= (select avg(sal) from emp where deptno = d.deptno)
6 ;
해석되었습니다.
경 과: 00:00:00.00
SQL> @plan
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------
Plan hash value: 3722278325
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 938 | 6 (17)| 00:00:01 |
|* 1 | VIEW | VW_WIF_1 | 14 | 938 | 6 (17)| 00:00:01 |
| 2 | WINDOW BUFFER | | 14 | 686 | 6 (17)| 00:00:01 |
| 3 | MERGE JOIN | | 14 | 686 | 6 (17)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| DEPT | 4 | 52 | 2 (0)| 00:00:01 |
| 5 | INDEX FULL SCAN | DEPT_DEPTNO_IDX | 4 | | 1 (0)| 00:00:01 |
|* 6 | SORT JOIN | | 14 | 504 | 4 (25)| 00:00:01 |
| 7 | TABLE ACCESS FULL | EMP | 14 | 504 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("VW_COL_6" IS NOT NULL)
6 - access("D"."DEPTNO"="E"."DEPTNO")
filter("D"."DEPTNO"="E"."DEPTNO")
21 개의 행이 선택되었습니다.
-- emp 테이블을 1000번 복제한 emp1, emp2 테이블 생성
create table emp1 as
select * from emp, (select rownum no from dual connect by level <= 1000);
create table emp2 as select * from emp1;
alter table emp1 add constraint emp1_pk primary key(no, empno);
alter table emp2 add constraint emp2_pk primary key(no, empno);
-- no_push_subq
select /*+ leading(e1) use_nl(e2) */
sum(e1.sal)
, sum(e2.sal)
from emp1 e1
, emp2 e2
where e1.no = e2.no
and e1.empno = e2.empno
and exists (select /*+ NO_UNNEST NO_PUSH_SUBQ */ 'x'
from dept
where deptno = e1.deptno
and loc = 'NEW YORK')
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.01 0.03 0 10 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.04 0.11 37 14348 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.06 0.14 37 14358 0 1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 91
Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE (cr=14348 pr=37 pw=37 time=0 us)
3000 FILTER (cr=14348 pr=37 pw=37 time=462 us)
14000 NESTED LOOPS (cr=14342 pr=35 pw=35 time=519 us)
14000 NESTED LOOPS (cr=342 pr=35 pw=35 time=286 us cost=12719 size=2002 card=22)
14000 TABLE ACCESS FULL EMP1 (cr=95 pr=0 pw=0 time=56 us cost=29 size=758472 card=14586)
14000 INDEX UNIQUE SCAN EMP2_PK (cr=247 pr=35 pw=35 time=0 us cost=0 size=0 card=1)(object id 91815)
14000 TABLE ACCESS BY INDEX ROWID EMP2 (cr=14000 pr=0 pw=0 time=0 us cost=1 size=39 card=1)
1 TABLE ACCESS BY INDEX ROWID DEPT (cr=6 pr=2 pw=2 time=0 us cost=2 size=11 card=1)
3 INDEX RANGE SCAN DEPT_DEPTNO_IDX (cr=3 pr=1 pw=1 time=0 us cost=1 size=0 card=1)(object id 91722)
-- push_subq
select /*+ leading(e1) use_nl(e2) */
sum(e1.sal)
, sum(e2.sal)
from emp1 e1
, emp2 e2
where e1.no = e2.no
and e1.empno = e2.empno
and exists (select /*+ NO_UNNEST PUSH_SUBQ */ 'x'
from dept
where deptno = e1.deptno
and loc = 'NEW YORK')
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 8 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.01 0.01 0 3348 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.01 0.02 0 3356 0 1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 91
Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE (cr=3348 pr=0 pw=0 time=0 us)
3000 NESTED LOOPS (cr=3348 pr=0 pw=0 time=157 us)
3000 NESTED LOOPS (cr=348 pr=0 pw=0 time=95 us cost=663 size=91 card=1)
3000 TABLE ACCESS FULL EMP1 (cr=101 pr=0 pw=0 time=37 us cost=29 size=37908 card=729)
1 TABLE ACCESS BY INDEX ROWID DEPT (cr=6 pr=0 pw=0 time=0 us cost=2 size=11 card=1)
3 INDEX RANGE SCAN DEPT_DEPTNO_IDX (cr=3 pr=0 pw=0 time=0 us cost=1 size=0 card=1)(object id 91722)
3000 INDEX UNIQUE SCAN EMP2_PK (cr=247 pr=0 pw=0 time=0 us cost=0 size=0 card=1)(object id 91815)
3000 TABLE ACCESS BY INDEX ROWID EMP2 (cr=3000 pr=0 pw=0 time=0 us cost=1 size=39 card=1)