테마
본사 유통 ERP는 외부 업체의 패키지다. 업체 설치 지침에 따라 ERP는 로그인하자마자 세션에 ALTER SESSION SET OPTIMIZER_MODE = FIRST_ROWS_10을 건다. 지침을 바꾸면 유지보수 계약에서 벗어나고, SQL은 암호화된 패키지 안에 있어 한 글자도 고칠 수 없다.
매일 아침 본부 담당자 서른 명이 「월 판매 명세」를 내려받는다(지난달 전체, 끝까지 받는다). 선배가 같은 글자의 SQL을 SQL Developer에서 한 번 돌려 보고 말했다. "SQL은 못 고쳐도 계획은 고칠 수 있지."
CF10 판매: 500,000행 · 4,583블록 · SLE_DT VARCHAR2(8) · 2023년 6월 20,953건 · IX_CF10_SLE_DT (SLE_DT) 리프 1,393 · 클러스터링 팩터 4,601(날짜순 적재)CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) 높이 3 · 판매 한 건에 평균 2.2줄(같은 블록)CF01 상품: 109행 · 5블록 · CF01_PK (ITM_NO)OPTIMIZER_MODE는 ALL_ROWS · 통계 최신 · SQL Plan Management 사용 가능(베이스라인은 아직 없다)SELECT a.SLE_NO, a.SLE_DT, a.SHP_NO, b.ITM_NO, c.ITM_NM, b.SLE_QTY, b.SLE_UPRC
FROM CF10 a, CF11 b, CF01 c
WHERE a.SLE_DT BETWEEN :1 AND :2
AND b.SLE_NO = a.SLE_NO
AND c.ITM_NO = b.ITM_NO
[ERP 세션 — '20230601' ~ '20230630' · 끝까지 받은 뒤]
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 45960 |00:00:00.97 | 176K|
|* 1 | FILTER | | 1 | | 45960 |00:00:00.97 | 176K|
| 2 | NESTED LOOPS | | 1 | 10 | 45960 |00:00:00.95 | 176K|
| 3 | NESTED LOOPS | | 1 | 10 | 45960 |00:00:00.71 | 130K|
| 4 | NESTED LOOPS | | 1 | 10 | 45960 |00:00:00.52 | 84500 |
| 5 | NESTED LOOPS | | 1 | 10 | 45960 |00:00:00.30 | 63112 |
| 6 | TABLE ACCESS BY INDEX ROWID | CF10 | 1 | 5 | 20953 |00:00:00.02 | 253 |
|* 7 | INDEX RANGE SCAN | IX_CF10_SLE_DT | 1 | 2673 | 20953 |00:00:00.01 | 60 |
|* 8 | INDEX RANGE SCAN | CF11_PK | 20953 | 2 | 45960 |00:00:00.21 | 62859 |
| 9 | TABLE ACCESS BY INDEX ROWID | CF11 | 45960 | 2 | 45960 |00:00:00.19 | 21388 |
|* 10 | INDEX UNIQUE SCAN | CF01_PK | 45960 | 1 | 45960 |00:00:00.11 | 45960 |
| 11 | TABLE ACCESS BY INDEX ROWID | CF01 | 45960 | 1 | 45960 |00:00:00.12 | 45960 |
------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(:1<=:2)
7 - access("A"."SLE_DT">=:1 AND "A"."SLE_DT"<=:2)
8 - access("B"."SLE_NO"="A"."SLE_NO")
10 - access("C"."ITM_NO"="B"."ITM_NO")
[V$SQL · V$SQL_OPTIMIZER_ENV — SQL_ID 6t1d0c4m8z2qk]
CHILD optimizer_mode PLAN_HASH_VALUE EXECUTIONS BUFFER_GETS/EXEC SQL_PLAN_BASELINE
0 first_rows_10 3871190422 31 176,420 (없음)
1 all_rows 912774650 1 8,464 (없음)
(자식 1 은 선배가 SQL Developer 에서 같은 글자로 한 번 돌린 것 · V$SQL_SHARED_CURSOR : OPTIMIZER_MODE_MISMATCH = Y)
[자식 1 — ALL_ROWS 세션]
------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Used-Mem |
------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 45960 |00:00:00.29 | 8464 | |
|* 1 | FILTER | | 1 | | 45960 |00:00:00.29 | 8464 | |
|* 2 | HASH JOIN | | 1 | 5858 | 45960 |00:00:00.28 | 8464 | 1236K (0)|
| 3 | TABLE ACCESS FULL | CF01 | 1 | 109 | 109 |00:00:00.01 | 7 | |
|* 4 | HASH JOIN | | 1 | 5858 | 45960 |00:00:00.25 | 8457 | 1653K (0)|
| 5 | TABLE ACCESS BY INDEX ROWID BATCHED| CF10 | 1 | 2673 | 20953 |00:00:00.02 | 253 | |
|* 6 | INDEX RANGE SCAN | IX_CF10_SLE_DT | 1 | 2673 | 20953 |00:00:00.01 | 60 | |
| 7 | TABLE ACCESS FULL | CF11 | 1 | 1096K| 1096K|00:00:00.11 | 8204 | |
------------------------------------------------------------------------------------------------------------------------
(176K = 176,420 · 130K = 130,460)