본문 바로가기
DBMS/SQL튜닝

oracle hint 적용 안되는 경우 확인 및 해결 법

by 드바 2023. 7. 7.
반응형
뷰 머징과 같은 쿼리변환(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 |      |      | |
-------------------------------------------------------------------------------------------------------------------------
반응형

댓글