PRECOMPUTE_SUBQUERY 힌트를 살펴보자.1
테스트를 위해 아래와 같이 테이블을 생성하자.
아래 쿼리는 가변 동적 조건을 처리하기 위해 임시 테이블을 사용했다.
PRECOMPUTE_SUBQUERY 힌트는 10.2.0.1 버전부터 사용할 수 있다.
임시 테이블에 30, 40 부서를 입력하고 쿼리를 다시 수행하면 Predicate Information이 변경된 것을 확인할 수 있다.
다행히 새로운 커서가 생성되지 않는다.
10053 트레이스를 확인해보면 옵티마이저 단계에 Parse 단계에서 변환된 쿼리가 입력되는 것을 확인할 수 있다.
마지막으로 테스트를 위해 생성한 emp_x1 인덱스를 삭제하자.
1 2 3 4 |
-- 1DROP 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 |
-- 2INSERT 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 |
-- 3SELECT name, version FROM v$sql_hint WHERE name = 'PRECOMPUTE_SUBQUERY';NAME VERSION------------------- --------PRECOMPUTE_SUBQUERY 10.2.0.11개의 행이 선택되었습니다. |
아래와 같이 힌트를 기술하면 실행 계획에서 dept_t 테이블에 대한 액세스가 사라지고, Predicate Information에 상수가 표시된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
-- 4SELECT /* 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 |
-- 5COMMIT;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 |
-- 6SELECT 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 01개의 행이 선택되었습니다. |
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 |
-- 7SELECT /*+ 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 0Execute 1 0.00 0.00 0 0 0 0Fetch 3 0.00 0.00 0 3 0 2------- ------ -------- ---------- ---------- ---------- ---------- ----------total 5 0.00 0.00 0 3 0 2Misses in library cache during parse: 0Optimizer mode: ALL_ROWSParsing user id: 110 (recursive depth: 1)Number of plan statistics captured: 1Rows (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 0Execute 1 0.00 0.00 0 0 0 0Fetch 2 0.00 0.00 0 5 0 6------- ------ -------- ---------- ---------- ---------- ---------- ----------total 4 0.00 0.00 0 9 0 6Misses in library cache during parse: 1Optimizer mode: ALL_ROWSParsing user id: 110Number of plan statistics captured: 1Rows (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 |
-- 9SELECT 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 |
-- 10DROP 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 |

