PRECOMPUTE_SUBQUERY 힌트를 살펴보자. 1
테스트를 위해 아래와 같이 테이블을 생성하자.
아래 쿼리는 가변 동적 조건을 처리하기 위해 임시 테이블을 사용했다.
PRECOMPUTE_SUBQUERY 힌트는 10.2.0.1 버전부터 사용할 수 있다.
임시 테이블에 30, 40 부서를 입력하고 쿼리를 다시 수행하면 Predicate Information이 변경된 것을 확인할 수 있다.
다행히 새로운 커서가 생성되지 않는다.
10053 트레이스를 확인해보면 옵티마이저 단계에 Parse 단계에서 변환된 쿼리가 입력되는 것을 확인할 수 있다.
마지막으로 테스트를 위해 생성한 emp_x1 인덱스를 삭제하자.
1 2 3 4 |
-- 1 DROP TABLE dept_t PURGE; CREATE GLOBAL TEMPORARY TABLE dept_t (deptno NUMBER (2)); CREATE INDEX emp_x1 ON emp (deptno); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
-- 2 INSERT INTO dept_t VALUES (10); INSERT INTO dept_t VALUES (20); SELECT a.* FROM emp a WHERE a.deptno IN ( SELECT x.deptno FROM dept_t x); ----------------------------------------------- | Id | Operation | Name | ----------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | NESTED LOOPS | | | 2 | NESTED LOOPS | | | 3 | SORT UNIQUE | | | 4 | TABLE ACCESS FULL | DEPT_T | |* 5 | INDEX RANGE SCAN | EMP_X1 | | 6 | TABLE ACCESS BY INDEX ROWID| EMP | ----------------------------------------------- |
1 2 3 4 5 6 7 8 |
-- 3 SELECT name , version FROM v$sql_hint WHERE name = 'PRECOMPUTE_SUBQUERY' ; NAME VERSION ------------------- -------- PRECOMPUTE_SUBQUERY 10.2.0.1 1개의 행이 선택되었습니다. |
아래와 같이 힌트를 기술하면 실행 계획에서 dept_t 테이블에 대한 액세스가 사라지고, Predicate Information에 상수가 표시된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
-- 4 SELECT /* PS_TEST */ a.* FROM emp a WHERE a.deptno IN ( SELECT /*+ PRECOMPUTE_SUBQUERY */ x.deptno FROM dept_t x); ------------------------------------------------------- | Id | Operation | Name | ------------------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | INLIST ITERATOR | | | 2 | TABLE ACCESS BY INDEX ROWID BATCHED| EMP | |* 3 | INDEX RANGE SCAN | EMP_X1 | ------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access( "A" . "DEPTNO" =10 OR "A" . "DEPTNO" =20) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
-- 5 COMMIT ; INSERT INTO dept_t VALUES (30); INSERT INTO dept_t VALUES (40); SELECT /* PS_TEST */ a.* FROM emp a WHERE a.deptno IN ( SELECT /*+ PRECOMPUTE_SUBQUERY */ x.deptno FROM dept_t x); ------------------------------------------------------- | Id | Operation | Name | ------------------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | INLIST ITERATOR | | | 2 | TABLE ACCESS BY INDEX ROWID BATCHED| EMP | |* 3 | INDEX RANGE SCAN | EMP_X1 | ------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access( "A" . "DEPTNO" =30 OR "A" . "DEPTNO" =40) |
1 2 3 4 5 6 7 8 9 10 11 |
-- 6 SELECT sql_id, child_number FROM v$sql WHERE sql_text LIKE '%PS_TEST%' AND sql_text NOT LIKE '%v$sql%' ; SQL_ID CHILD_NUMBER ------------- ------------ 284p70ccnzczp 0 1개의 행이 선택되었습니다. |
10046 트레이스를 확인해보면 동작 원리를 이해할 수 있다. Parse 단계에서 dept_t 테이블을 조회하는 Recursive 쿼리가 수행되는 것을 확인할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
-- 7 SELECT /*+ BYPASS_RECURSIVE_CHECK */ DISTINCT * FROM ( SELECT /*+ PRECOMPUTE_SUBQUERY */ x.deptno FROM dept_t x) 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 3 0.00 0.00 0 3 0 2 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 5 0.00 0.00 0 3 0 2 Misses in library cache during parse: 0 Optimizer mode: ALL_ROWS Parsing user id: 110 (recursive depth: 1) Number of plan statistics captured: 1 Rows (1st) Rows ( avg ) Rows ( max ) Row Source Operation ---------- ---------- ---------- --------------------------------------------------- 2 2 2 HASH UNIQUE (cr=3 pr=0 pw=0 time =163 us cost=3 size =13 card=1) 2 2 2 TABLE ACCESS FULL DEPT_T (cr=3 pr=0 pw=0 time =36 us cost=2 size =13 card=1) ******************************************************************************** SELECT /* PS_TEST */ a.* FROM emp a WHERE a.deptno IN ( SELECT /*+ PRECOMPUTE_SUBQUERY */ x.deptno FROM dept_t x) call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 4 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 2 0.00 0.00 0 5 0 6 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 4 0.00 0.00 0 9 0 6 Misses in library cache during parse: 1 Optimizer mode: ALL_ROWS Parsing user id: 110 Number of plan statistics captured: 1 Rows (1st) Rows ( avg ) Rows ( max ) Row Source Operation ---------- ---------- ---------- --------------------------------------------------- 6 6 6 INLIST ITERATOR (cr=5 pr=0 pw=0 time =24 us) 6 6 6 TABLE ACCESS BY INDEX ROWID BATCHED EMP (cr=5 pr=0 pw=0 time =35 us cost=3 size =228 card=6) 6 6 6 INDEX RANGE SCAN EMP_X1 (cr=3 pr=0 pw=0 time =100 us cost=1 size =0 card=6)(object id 108098) |
1 2 3 4 5 6 7 8 9 |
-- 8 ===================================== SPD: BEGIN context at statement level ===================================== Stmt: ******* UNPARSED QUERY IS ******* SELECT "A" . "EMPNO" "EMPNO" , "A" . "ENAME" "ENAME" , "A" . "JOB" "JOB" , "A" . "MGR" "MGR" , "A" . "HIREDATE" "HIREDATE" , "A" . "SAL" "SAL" , "A" . "COMM" "COMM" , "A" . "DEPTNO" "DEPTNO" FROM "SCOTT" . "EMP" "A" WHERE "A" . "DEPTNO" =40 OR "A" . "DEPTNO" =30 |
서브 쿼리에 바인드 변수를 사용하면 해당 힌트가 동작하지 않는다. 쿼리 변환은 Parse 단계에서 수행되는데, 바인딩은 Execute 단계에서 수행되기 때문이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
-- 9 SELECT a.* FROM emp a WHERE a.deptno IN ( SELECT /*+ PRECOMPUTE_SUBQUERY */ x.deptno FROM dept x WHERE x.loc = :v_loc); ----------------------------------------------- | Id | Operation | Name | ----------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | NESTED LOOPS | | | 2 | NESTED LOOPS | | |* 3 | TABLE ACCESS FULL | DEPT | |* 4 | INDEX RANGE SCAN | EMP_X1 | | 5 | TABLE ACCESS BY INDEX ROWID| EMP | ----------------------------------------------- |
1 2 |
-- 10 DROP INDEX emp_x1; |
- 해당 힌트는 Undocumented Hint이므로 운영환경에서 사용하시면 안된다. 개인 스크립트에만 활용하도록 하자. [본문으로]
'Oracle > Tuning' 카테고리의 다른 글
PX SEND 1 SLAVE 오퍼레이션 (0) | 2018.12.21 |
---|---|
PX SELECTOR 오퍼레이션 (0) | 2018.12.19 |
DB 링크 튜닝 기법 (0) | 2018.12.13 |
JPPD와 서브 쿼리 팩토링 (0) | 2018.12.11 |
JPPD와 ROLLUP (0) | 2018.12.10 |