스칼라서브쿼리 |
---|
{code:sql} SQL> SELECT empno, ename, sal, hiredate 2 , (SELECT d.dname FROM dept d WHERE d.deptno = e.deptno) dname 3 FROM emp e 4 WHERE sal >= 2000 5 ; |
EMPNO ENAME SAL HIREDATE DNAME
6 개의 행이 선택되었습니다.
Execution Plan
Id | Operation | Name | Rows | Bytes | Cost (%CPU) | Time |
0 | SELECT STATEMENT | 6 | 150 | 3 (0) | 00:00:01 | |
1 | TABLE ACCESS BY INDEX ROWID | DEPT | 1 | 10 | 1 (0) | 00:00:01 |
| INDEX UNIQUE SCAN | PK_DEPT | 1 | 0 (0) | 00:00:01 | |
| TABLE ACCESS FULL | EMP | 6 | 150 | 3 (0) | 00:00:01 |
Predicate Information (identified by operation id):
2 - access("D"."DEPTNO"=:B1)
3 - filter("SAL">=2000)
Statistics
SQL>
|
|| 아우터조인 ||
|{code:sql}
SQL> SELECT e.empno, e.ename, e.sal, e.hiredate
2 , d.dname
3 FROM emp e
4 , dept d
5 WHERE e.deptno = d.deptno(+)
6 AND e.sal >= 2000
7 ;
EMPNO ENAME SAL HIREDATE DNAME
---------- ---------- ---------- -------- --------------
7839 KING 5000 81/11/17 ACCOUNTING
7782 CLARK 2450 81/06/09 ACCOUNTING
7902 FORD 3000 81/12/03 RESEARCH
7788 SCOTT 3000 87/04/19 RESEARCH
7566 JONES 2975 81/04/02 RESEARCH
7698 BLAKE 2850 81/05/01 SALES
6 개의 행이 선택되었습니다.
Execution Plan
----------------------------------------------------------
Plan hash value: 3387915970
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6 | 210 | 7 (15)| 00:00:01 |
|* 1 | HASH JOIN OUTER | | 6 | 210 | 7 (15)| 00:00:01 |
|* 2 | TABLE ACCESS FULL| EMP | 6 | 150 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| DEPT | 6 | 60 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("E"."DEPTNO"="D"."DEPTNO"(+))
2 - filter("E"."SAL">=2000)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
15 consistent gets
0 physical reads
0 redo size
882 bytes sent via SQL*Net to client
415 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
6 rows processed
SQL>
|
아우터조인(힌트사용) |
---|
{code:sql} SQL> SELECT /*+ ordered use_nl(d) */ 2 e.empno, e.ename, e.sal, e.hiredate 3 , d.dname 4 FROM emp e 5 , dept d 6 WHERE e.deptno = d.deptno(+) 7 AND e.sal >= 2000 8 ; |
EMPNO ENAME SAL HIREDATE DNAME
6 개의 행이 선택되었습니다.
Execution Plan
Id | Operation | Name | Rows | Bytes | Cost (%CPU) | Time |
0 | SELECT STATEMENT | 6 | 210 | 9 (0) | 00:00:01 | |
1 | NESTED LOOPS OUTER | 6 | 210 | 9 (0) | 00:00:01 | |
| TABLE ACCESS FULL | EMP | 6 | 150 | 3 (0) | 00:00:01 |
3 | TABLE ACCESS BY INDEX ROWID | DEPT | 1 | 10 | 1 (0) | 00:00:01 |
| INDEX UNIQUE SCAN | PK_DEPT | 1 | 0 (0) | 00:00:01 |
Predicate Information (identified by operation id):
2 - filter("E"."SAL">=2000)
4 - access("E"."DEPTNO"="D"."DEPTNO"(+))
Statistics
SQL>
|
h2. (2) 스칼라 서브쿼리 캐싱 효과
- 오라클은 스칼라서브쿼리의 수행횟수를 최소화 하려고 입력값과 출력값을 내부캐시(Query Execution Cache)에 저장해 둔다
- 스칼라 서브쿼리 수행 => "입력값"을 캐시에서 찾아
-- 있으면 => "출력값"을 리턴
-- 없으면 => 쿼리 수행 결과 리턴 => 입출력 값을 캐시에 저장
|| 스칼라서브쿼리 (입력값/출력값) ||
|{code:sql}
SELECT empno, ename, sal, hiredate
, (SELECT d.dname -- 출력 값 : d.dname
FROM dept d
WHERE d.deptno = e.deptno -- 입력 값 : e.deptno
) dname
FROM emp e
WHERE sal >= 2000
;
|
스칼라서브쿼리를 이용한 함수 호출 횟수 줄이기 |
---|
{code:sql} SELECT empno, ename, sal, hiredate , get_dname(deptno) dname1 -- 일반함수 : 캐싱 효과 없음 , get_dname_Determi(deptno) dname2 -- Deterministic 함수 : CGA 에 캐싱, DB Call 내에서만 유효 , (SELECT get_dname(deptno) FROM dual) dname3 -- 스칼라 서브쿼리 : UGA 에 캐싱, 쿼리 끝날때까지 유효 FROM emp e WHERE sal >= 2000 ; {code} |
1. 쿼리 변환(View Merging) 발생 => 예상 했던 emp 풀스캔 비효율이 없음 |
---|
{code:sql} SQL> SELECT d.deptno 2 , d.dname 3 , e.avg_sal 4 , e.min_sal 5 , e.max_sal 6 FROM dept d 7 , (SELECT deptno 8 , AVG(sal) avg_sal 9 , MIN(sal) min_sal 10 , MAX(sal) max_sal 11 FROM emp 12 GROUP BY deptno 13 ) e 14 WHERE d.deptno = e.deptno(+) 15 AND d.loc = 'CHICAGO' 16 ; |
DEPTNO DNAME AVG_SAL MIN_SAL MAX_SAL
Execution Plan
Id | Operation | Name | Rows | Bytes | Cost (%CPU) | Time |
0 | SELECT STATEMENT | 2 | 46 | 8 (25) | 00:00:01 | |
1 | HASH GROUP BY | 2 | 46 | 8 (25) | 00:00:01 | |
| HASH JOIN OUTER | 5 | 115 | 7 (15) | 00:00:01 | |
| TABLE ACCESS FULL | DEPT | 1 | 16 | 3 (0) | 00:00:01 |
4 | TABLE ACCESS FULL | EMP | 14 | 98 | 3 (0) | 00:00:01 |
Predicate Information (identified by operation id):
2 - access("D"."DEPTNO"="DEPTNO"(+))
3 - filter("D"."LOC"='CHICAGO')
Statistics
SQL>
|
|| 2. NO_MERGE 힌트 사용 => emp 풀스캔 비효율, 예상했던 11G의 Join Predicate Pushdown 은 발생 안함 ||
|{code:sql}
SQL> SELECT d.deptno
2 , d.dname
3 , e.avg_sal
4 , e.min_sal
5 , e.max_sal
6 FROM dept d
7 , (SELECT /*+ no_merge */
8 deptno
9 , AVG(sal) avg_sal
10 , MIN(sal) min_sal
11 , MAX(sal) max_sal
12 FROM emp
13 GROUP BY deptno
14 ) e
15 WHERE d.deptno = e.deptno(+)
16 AND d.loc = 'CHICAGO'
17 ;
DEPTNO DNAME AVG_SAL MIN_SAL MAX_SAL
---------- -------------- ---------- ---------- ----------
30 SALES 1566.66667 950 2850
Execution Plan
----------------------------------------------------------
Plan hash value: 2939036968
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 68 | 8 (25)| 00:00:01 |
|* 1 | HASH JOIN OUTER | | 1 | 68 | 8 (25)| 00:00:01 |
|* 2 | TABLE ACCESS FULL | DEPT | 1 | 16 | 3 (0)| 00:00:01 |
| 3 | VIEW | | 3 | 156 | 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 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("D"."DEPTNO"="E"."DEPTNO"(+))
2 - filter("D"."LOC"='CHICAGO')
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
14 consistent gets
0 physical reads
0 redo size
711 bytes sent via SQL*Net to client
415 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL>
|
3. Index 생성 => Join Predicate Pushdown 발생 |
---|
{code:sql} SQL> CREATE INDEX idx_emp_deptno ON emp(deptno); |
인덱스가 생성되었습니다.
SQL> SELECT d.deptno
2 , d.dname
3 , e.avg_sal
4 , e.min_sal
5 , e.max_sal
6 FROM dept d
7 , (SELECT /*+ no_merge */
8 deptno
9 , AVG(sal) avg_sal
10 , MIN(sal) min_sal
11 , MAX(sal) max_sal
12 FROM emp
13 GROUP BY deptno
14 ) e
15 WHERE d.deptno = e.deptno(+)
16 AND d.loc = 'CHICAGO'
17 ;
DEPTNO DNAME AVG_SAL MIN_SAL MAX_SAL
Execution Plan
Id | Operation | Name | Rows | Bytes | Cost (%CPU) | Time |
0 | SELECT STATEMENT | 1 | 55 | 5 (0) | 00:00:01 | |
1 | NESTED LOOPS OUTER | 1 | 55 | 5 (0) | 00:00:01 | |
| TABLE ACCESS FULL | DEPT | 1 | 16 | 3 (0) | 00:00:01 |
3 | VIEW PUSHED PREDICATE | 1 | 39 | 2 (0) | 00:00:01 | |
| FILTER | |||||
5 | SORT AGGREGATE | 1 | 7 | |||
6 | TABLE ACCESS BY INDEX ROWID | EMP | 5 | 35 | 2 (0) | 00:00:01 |
| INDEX RANGE SCAN | IDX_EMP_DEPTNO | 5 | 1 (0) | 00:00:01 |
Predicate Information (identified by operation id):
2 - filter("D"."LOC"='CHICAGO')
4 - filter(COUNT(*)>0)
7 - access("DEPTNO"="D"."DEPTNO")
Statistics
SQL>
|
|| 스칼라서브쿼리 오류 발생 => 단일값만 리턴해야 함 ||
|{code:sql}
SQL> SELECT d.deptno
2 , d.dname
3 , (SELECT AVG(sal) avg_sal
4 , MIN(sal) min_sal
5 , MAX(sal) max_sal
6 FROM emp e
7 WHERE e.deptno = d.deptno
8 ) sal
9 FROM dept d
10 WHERE d.loc = 'CHICAGO'
11 ;
, (SELECT AVG(sal) avg_sal
*
3행에 오류:
ORA-00913: 값의 수가 너무 많습니다
SQL>
|
스칼라서브쿼리 반복 사용 비효율 |
---|
{code:sql} SQL> SELECT d.deptno 2 , d.dname 3 , (SELECT AVG(sal) FROM emp WHERE deptno = d.deptno) avg_sal 4 , (SELECT MIN(sal) FROM emp WHERE deptno = d.deptno) min_sal 5 , (SELECT MAX(sal) FROM emp WHERE deptno = d.deptno) max_sal 6 FROM dept d 7 WHERE d.loc = 'CHICAGO' 8 ; |
DEPTNO DNAME AVG_SAL MIN_SAL MAX_SAL
Execution Plan
Id | Operation | Name | Rows | Bytes | Cost (%CPU) | Time |
0 | SELECT STATEMENT | 1 | 16 | 3 (0) | 00:00:01 | |
1 | SORT AGGREGATE | 1 | 7 | |||
2 | TABLE ACCESS BY INDEX ROWID | EMP | 5 | 35 | 2 (0) | 00:00:01 |
| INDEX RANGE SCAN | IDX_EMP_DEPTNO | 5 | 1 (0) | 00:00:01 | |
4 | SORT AGGREGATE | 1 | 7 | |||
5 | TABLE ACCESS BY INDEX ROWID | EMP | 5 | 35 | 2 (0) | 00:00:01 |
| INDEX RANGE SCAN | IDX_EMP_DEPTNO | 5 | 1 (0) | 00:00:01 | |
7 | SORT AGGREGATE | 1 | 7 | |||
8 | TABLE ACCESS BY INDEX ROWID | EMP | 5 | 35 | 2 (0) | 00:00:01 |
| INDEX RANGE SCAN | IDX_EMP_DEPTNO | 5 | 1 (0) | 00:00:01 | |
| TABLE ACCESS FULL | DEPT | 1 | 16 | 3 (0) | 00:00:01 |
Predicate Information (identified by operation id):
3 - access("DEPTNO"=:B1)
6 - access("DEPTNO"=:B1)
9 - access("DEPTNO"=:B1)
10 - filter("D"."LOC"='CHICAGO')
Statistics
SQL>
|
|| 여러 값을 붙여서 스칼라서브쿼리 한번만 사용 ||
|{code:sql}
SQL> SELECT deptno
2 , dname
3 , TO_NUMBER(SUBSTR(sal, 1, 7)) avg_sal
4 , TO_NUMBER(SUBSTR(sal, 8, 7)) min_sal
5 , TO_NUMBER(SUBSTR(sal, 15, 7)) max_sal
6 FROM (SELECT d.deptno
7 , d.dname
8 , (SELECT LPAD(AVG(sal), 7)
9 || LPAD(MIN(sal), 7)
10 || LPAD(MAX(sal), 7)
11 FROM emp
12 WHERE deptno = d.deptno
13 ) sal
14 FROM dept d
15 WHERE d.loc = 'CHICAGO'
16 )
17 ;
DEPTNO DNAME AVG_SAL MIN_SAL MAX_SAL
---------- -------------- ---------- ---------- ----------
30 SALES 1566.66 950 2850
Execution Plan
----------------------------------------------------------
Plan hash value: 3370201435
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 16 | 3 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 7 | | |
| 2 | TABLE ACCESS BY INDEX ROWID| EMP | 5 | 35 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_EMP_DEPTNO | 5 | | 1 (0)| 00:00:01 |
|* 4 | TABLE ACCESS FULL | DEPT | 1 | 16 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("DEPTNO"=:B1)
4 - filter("D"."LOC"='CHICAGO')
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
10 consistent gets
0 physical reads
0 redo size
694 bytes sent via SQL*Net to client
415 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL>
|
Object Type 사용 |
---|
{code:sql} SQL> CREATE OR REPLACE TYPE sal_type 2 AS OBJECT(avg_sal NUMBER, min_sal NUMBER, max_sal NUMBER) 3 / |
유형이 생성되었습니다.
SQL> SELECT deptno
2 , dname
3 , a.sal.avg_sal avg_sal
4 , a.sal.min_sal min_sal
5 , a.sal.max_sal max_sal
6 FROM (SELECT d.deptno
7 , d.dname
8 , (SELECT sal_type(AVG(sal), MIN(sal), MAX(sal))
9 FROM emp
10 WHERE deptno = d.deptno
11 ) sal
12 FROM dept d
13 WHERE d.loc = 'CHICAGO'
14 ) a
15 ;
DEPTNO DNAME AVG_SAL MIN_SAL MAX_SAL
Execution Plan
Id | Operation | Name | Rows | Bytes | Cost (%CPU) | Time -- | 0 | SELECT STATEMENT | 1 | 16 | 3 (0) | 00:00:01 | 1 | SORT AGGREGATE | 1 | 7 | ||||
2 | TABLE ACCESS BY INDEX ROWID | EMP | 5 | 35 | 2 (0) | 00:00:01 |
| INDEX RANGE SCAN | IDX_EMP_DEPTNO | 5 | 1 (0) | 00:00:01 |
| TABLE ACCESS FULL | DEPT | 1 | 16 | 3 (0) | 00:00:01 -- |
Predicate Information (identified by operation id):
3 - access("DEPTNO"=:B1)
4 - filter("D"."LOC"='CHICAGO')
Statistics
SQL>
|