뷰 머징과 같은 쿼리변환(Query Transformation)으로 힌트가 적용되지 않는 경우 확인 및 해결 법 |
SQL 수행
-- 테스트 뷰 생성
CREATE VIEW HR.DP_V AS
SELECT DP.*
FROM(SELECT *
FROM HR.DEPARTMENTS
UNION ALL
SELECT *
FROM HR.DEPARTMENTS_TMP
) DP, EMPLOYEES E, LOCATIONS L
WHERE DP.DEPARTMENT_ID = E.DEPARTMENT_ID
AND DP.LOCATION_ID = L.LOCATION_ID
;
-- SQL
SELECT E.* FROM HR.EMPLOYEES E, HR.DP_V D
WHERE 1=1
AND E.DEPARTMENT_ID = D.DEPARTMENT_ID
AND D.DEPARTMENT_NAME LIKE 'Shi%'
;
---------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4050 |00:00:00.01 | 550 |
| 1 | NESTED LOOPS | | 1 | 4050 |00:00:00.01 | 550 |
| 2 | NESTED LOOPS | | 1 | 90 |00:00:00.01 | 190 |
| 3 | NESTED LOOPS | | 1 | 2 |00:00:00.01 | 8 |
| 4 | VIEW | | 1 | 2 |00:00:00.01 | 6 |
| 5 | UNION-ALL | | 1 | 2 |00:00:00.01 | 6 |
| 6 | TABLE ACCESS BY INDEX ROWID BATCHED| DEPARTMENTS | 1 | 1 |00:00:00.01 | 3 |
|* 7 | INDEX RANGE SCAN | DEPARTMENTS_IX01 | 1 | 1 |00:00:00.01 | 2 |
|* 8 | TABLE ACCESS FULL | DEPARTMENTS_TMP | 1 | 1 |00:00:00.01 | 3 |
|* 9 | INDEX UNIQUE SCAN | LOC_ID_PK | 2 | 2 |00:00:00.01 | 2 |
| 10 | TABLE ACCESS BY INDEX ROWID BATCHED | EMPLOYEES | 2 | 90 |00:00:00.01 | 182 |
|* 11 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 2 | 90 |00:00:00.01 | 92 |
|* 12 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 90 | 4050 |00:00:00.01 | 360 |
---------------------------------------------------------------------------------------------------------------
조인 순서를 e -> d 로 변경해보자
SELECT /*+ leading(e d) */ E.* FROM HR.EMPLOYEES E, HR.DP_V D
WHERE 1=1
AND E.DEPARTMENT_ID = D.DEPARTMENT_ID
AND D.DEPARTMENT_NAME LIKE 'Shi%'
;
-- 힌트가 적용되지 않았다
---------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4050 |00:00:00.01 | 550 |
| 1 | NESTED LOOPS | | 1 | 4050 |00:00:00.01 | 550 |
| 2 | NESTED LOOPS | | 1 | 90 |00:00:00.01 | 190 |
| 3 | NESTED LOOPS | | 1 | 2 |00:00:00.01 | 8 |
| 4 | VIEW | | 1 | 2 |00:00:00.01 | 6 |
| 5 | UNION-ALL | | 1 | 2 |00:00:00.01 | 6 |
| 6 | TABLE ACCESS BY INDEX ROWID BATCHED| DEPARTMENTS | 1 | 1 |00:00:00.01 | 3 |
|* 7 | INDEX RANGE SCAN | DEPARTMENTS_IX01 | 1 | 1 |00:00:00.01 | 2 |
|* 8 | TABLE ACCESS FULL | DEPARTMENTS_TMP | 1 | 1 |00:00:00.01 | 3 |
|* 9 | INDEX UNIQUE SCAN | LOC_ID_PK | 2 | 2 |00:00:00.01 | 2 |
| 10 | TABLE ACCESS BY INDEX ROWID BATCHED | EMPLOYEES | 2 | 90 |00:00:00.01 | 182 |
|* 11 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 2 | 90 |00:00:00.01 | 92 |
|* 12 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 90 | 4050 |00:00:00.01 | 360 |
---------------------------------------------------------------------------------------------------------------
-- 원인은 Outline Data MERGE(@"SEL$2" >"SEL$1") 부분에서 확인 가능(쿼리 변환 발생)
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$F5BB74E1
4 - SET$1 / DP@SEL$2
5 - SET$1
6 - SEL$3 / DEPARTMENTS@SEL$3
7 - SEL$3 / DEPARTMENTS@SEL$3
8 - SEL$4 / DEPARTMENTS_TMP@SEL$4
9 - SEL$F5BB74E1 / L@SEL$2
10 - SEL$F5BB74E1 / E@SEL$1
11 - SEL$F5BB74E1 / E@SEL$1
12 - SEL$F5BB74E1 / E@SEL$2
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('19.1.0')
DB_VERSION('19.1.0')
OPT_PARAM('_px_adaptive_dist_method' 'off')
OPT_PARAM('_optimizer_strans_adaptive_pruning' 'false')
OPT_PARAM('_optimizer_nlj_hj_adaptive_join' 'false')
ALL_ROWS
OUTLINE_LEAF(@"SEL$3")
OUTLINE_LEAF(@"SEL$4")
OUTLINE_LEAF(@"SET$1")
OUTLINE_LEAF(@"SEL$F5BB74E1")
MERGE(@"SEL$2" >"SEL$1") <-- 뷰 머징이 발생 하였다
OUTLINE(@"SEL$1")
OUTLINE(@"SEL$2")
NO_ACCESS(@"SEL$F5BB74E1" "DP"@"SEL$2")
INDEX(@"SEL$F5BB74E1" "L"@"SEL$2" ("LOCATIONS"."LOCATION_ID"))
INDEX_RS_ASC(@"SEL$F5BB74E1" "E"@"SEL$1" ("EMPLOYEES"."DEPARTMENT_ID"))
BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$F5BB74E1" "E"@"SEL$1")
INDEX(@"SEL$F5BB74E1" "E"@"SEL$2" ("EMPLOYEES"."DEPARTMENT_ID"))
LEADING(@"SEL$F5BB74E1" "DP"@"SEL$2" "L"@"SEL$2" "E"@"SEL$1" "E"@"SEL$2")
USE_NL(@"SEL$F5BB74E1" "L"@"SEL$2")
USE_NL(@"SEL$F5BB74E1" "E"@"SEL$1")
USE_NL(@"SEL$F5BB74E1" "E"@"SEL$2")
FULL(@"SEL$4" "DEPARTMENTS_TMP"@"SEL$4")
INDEX_RS_ASC(@"SEL$3" "DEPARTMENTS"@"SEL$3" ("DEPARTMENTS"."DEPARTMENT_NAME"))
BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$3" "DEPARTMENTS"@"SEL$3")
END_OUTLINE_DATA
*/
반응형
no_merge 힌트로 뷰머징이 발생하지 않도록 변경
SELECT /*+ no_merge(d) leading(e d) */ E.* FROM HR.EMPLOYEES E, HR.DP_V D
WHERE 1=1
AND E.DEPARTMENT_ID = D.DEPARTMENT_ID
AND D.DEPARTMENT_NAME LIKE 'Shi%'
;
-- 조인 순서 변경 힌트가 잘 적용되었다
-------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
-------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4050 |00:00:00.01 | 106 | | | |
|* 1 | HASH JOIN | | 1 | 4050 |00:00:00.01 | 106 | 932K| 932K| 1232K (0)|
| 2 | TABLE ACCESS FULL | EMPLOYEES | 1 | 107 |00:00:00.01 | 6 | | | |
| 3 | VIEW | DP_V | 1 | 90 |00:00:00.01 | 100 | | | |
| 4 | NESTED LOOPS | | 1 | 90 |00:00:00.01 | 100 | | | |
| 5 | NESTED LOOPS | | 1 | 2 |00:00:00.01 | 8 | | | |
| 6 | VIEW | | 1 | 2 |00:00:00.01 | 6 | | | |
| 7 | UNION-ALL | | 1 | 2 |00:00:00.01 | 6 | | | |
| 8 | TABLE ACCESS BY INDEX ROWID BATCHED| DEPARTMENTS | 1 | 1 |00:00:00.01 | 3 | | | |
|* 9 | INDEX RANGE SCAN | DEPARTMENTS_IX01 | 1 | 1 |00:00:00.01 | 2 | | | |
|* 10 | TABLE ACCESS FULL | DEPARTMENTS_TMP | 1 | 1 |00:00:00.01 | 3 | | | |
|* 11 | INDEX UNIQUE SCAN | LOC_ID_PK | 2 | 2 |00:00:00.01 | 2 | | | |
|* 12 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 2 | 90 |00:00:00.01 | 92 | | | |
-------------------------------------------------------------------------------------------------------------------------------------------
DP_V 뷰 내부의 조인 순서를 변경해 보자(dp->l->e) -> (l->dp->e)
SELECT /*+ no_merge(d) leading(e d) leading(d.l d.dp d.e) */ E.* FROM HR.EMPLOYEES E, HR.DP_V D
WHERE 1=1
AND E.DEPARTMENT_ID = D.DEPARTMENT_ID
AND D.DEPARTMENT_NAME LIKE 'Shi%'
;
-- (d.dp)와 같은 글로벌 힌트를 사용해서 뷰 내부 오브젝트에 힌트를 적용할 수 있다
-------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
-------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4050 |00:00:00.01 | 105 | | | |
|* 1 | HASH JOIN | | 1 | 4050 |00:00:00.01 | 105 | 932K| 932K| 1208K (0)|
| 2 | TABLE ACCESS FULL | EMPLOYEES | 1 | 107 |00:00:00.01 | 6 | | | |
| 3 | VIEW | DP_V | 1 | 90 |00:00:00.01 | 99 | | | |
| 4 | NESTED LOOPS | | 1 | 90 |00:00:00.01 | 99 | | | |
|* 5 | HASH JOIN | | 1 | 2 |00:00:00.01 | 7 | 2546K| 2546K| 1485K (0)|
| 6 | INDEX FULL SCAN | LOC_ID_PK | 1 | 23 |00:00:00.01 | 1 | | | |
| 7 | VIEW | | 1 | 2 |00:00:00.01 | 6 | | | |
| 8 | UNION-ALL | | 1 | 2 |00:00:00.01 | 6 | | | |
| 9 | TABLE ACCESS BY INDEX ROWID BATCHED| DEPARTMENTS | 1 | 1 |00:00:00.01 | 3 | | | |
|* 10 | INDEX RANGE SCAN | DEPARTMENTS_IX01 | 1 | 1 |00:00:00.01 | 2 | | | |
|* 11 | TABLE ACCESS FULL | DEPARTMENTS_TMP | 1 | 1 |00:00:00.01 | 3 | | | |
|* 12 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 2 | 90 |00:00:00.01 | 92 | | | |
-------------------------------------------------------------------------------------------------------------------------------------------
뷰(dp_v) 내부 인라인뷰(dp)의 DEPARTMENTS 테이블에 힌트를 적용해보자(index -> table full scan)
SELECT /*+ no_merge(d) leading(e d) leading(d.l d.dp d.e) full(d.dp.DEPARTMENTS) */ E.* FROM HR.EMPLOYEES E, HR.DP_V D
WHERE 1=1
AND E.DEPARTMENT_ID = D.DEPARTMENT_ID
AND D.DEPARTMENT_NAME LIKE 'Shi%'
;
-- INDEX RANGE SCAN -> TABLE ACCESS FULL 변경됨
-------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
-------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4050 |00:00:00.01 | 109 | | | |
|* 1 | HASH JOIN | | 1 | 4050 |00:00:00.01 | 109 | 932K| 932K| 1232K (0)|
| 2 | TABLE ACCESS FULL | EMPLOYEES | 1 | 107 |00:00:00.01 | 6 | | | |
| 3 | VIEW | DP_V | 1 | 90 |00:00:00.01 | 103 | | | |
| 4 | NESTED LOOPS | | 1 | 90 |00:00:00.01 | 103 | | | |
|* 5 | HASH JOIN | | 1 | 2 |00:00:00.01 | 11 | 2546K| 2546K| 1689K (0)|
| 6 | INDEX FULL SCAN | LOC_ID_PK | 1 | 23 |00:00:00.01 | 1 | | | |
| 7 | VIEW | | 1 | 2 |00:00:00.01 | 10 | | | |
| 8 | UNION-ALL | | 1 | 2 |00:00:00.01 | 10 | | | |
|* 9 | TABLE ACCESS FULL| DEPARTMENTS | 1 | 1 |00:00:00.01 | 7 | | | |
|* 10 | TABLE ACCESS FULL| DEPARTMENTS_TMP | 1 | 1 |00:00:00.01 | 3 | | | |
|* 11 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 2 | 90 |00:00:00.01 | 92 | | | |
-------------------------------------------------------------------------------------------------------------------------
'DBMS > SQL튜닝' 카테고리의 다른 글
rollup, grouping sets, cube, grouping 함수 (0) | 2023.07.30 |
---|---|
oracle ABS 함수, 절대값을 구하는 함수 (0) | 2023.07.18 |
oracle hint 위치별 적용여부 테스트 (0) | 2023.07.04 |
ORDER BY 컬럼명이 가리키는 컬럼은? (0) | 2023.07.03 |
펑션 위치별 실행 횟 수 (0) | 2023.07.02 |
댓글