테마
「매장 월 상품 판매」는 매장 하나와 달 하나를 고르면 그 매장에서 그달 팔린 상품마다 영수증 수와 판매 수량, 금액을 낸다. 점장 여섯 명과 본사 영업팀이 달을 바꿔 가며 하루 300번쯤 연다. 2년 전 선배가 이 SQL에 힌트를 달고 계획이 해시 조인인 것을 확인했다. 힌트 대장에는 "해시 조인으로 고정, 8,682블록"이라고 적혀 있다. 지난 토요일 DBA 강도현이 1년 넘게 갱신되지 않던 판매 테이블들의 통계를 새로 모았다. 월요일부터 이 리포트가 0.17초에서 0.41초로 늘었다. 박팀장은 "힌트를 더 달아서 막을지, 힌트를 떼고 통계와 베이스라인에 맡길지 정해 와라"라고 했다. 아래는 같은 매장의 2023년 6월을 지난주와 이번 주에 돌린 실행계획이다.
CF10 판매: 500,000행 · 4,583블록 · IX_CF10_SLE_DT (SLE_DT) 리프 1,393블록 · 날짜순 적재 · 통계는 매주 모아 왔다CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) BLEVEL 2 · 영수증당 평균 2.19줄 · 영수증 번호 순으로 적재CF11 통계: 지난주까지는 2022년 12월 말에 모은 값(548,120행 · 4,110블록)이었고, 지난 토요일 1,096,708행 · 8,204블록으로 바뀌었다SLE_DT가 VARCHAR2(8)이라 옵티마이저는 6월 한 달을 5,177건으로, 이 매장의 6월을 863건으로 본다(교재 03화)SELECT /*+ LEADING(a b) */
b.ITM_NO,
COUNT(DISTINCT a.SLE_NO) AS rcpt_cnt,
SUM(b.SLE_QTY) AS sle_qty,
SUM(b.SLE_QTY * b.SLE_UPRC) AS sle_amt
FROM CF10 a, CF11 b
WHERE a.SHP_NO = :v_shp
AND a.SLE_DT BETWEEN :d1 AND :d2
AND b.SLE_NO = a.SLE_NO
GROUP BY b.ITM_NO
ORDER BY b.ITM_NO
[지난주 월요일 · 통계 갱신 전]
-------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Used-Mem |
-------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 104 |00:00:00.17 | 8682 | |
| 1 | SORT GROUP BY | | 1 | 104 | 104 |00:00:00.17 | 8682 | 372K (0)|
|* 2 | HASH JOIN | | 1 | 1890 | 7656 |00:00:00.16 | 8682 | 1421K (0)|
|* 3 | TABLE ACCESS BY INDEX ROWID BATCHED | CF10 | 1 | 863 | 3492 |00:00:00.02 | 478 | |
|* 4 | INDEX RANGE SCAN | IX_CF10_SLE_DT | 1 | 5177 | 20953 |00:00:00.01 | 60 | |
| 5 | TABLE ACCESS FULL | CF11 | 1 | 548K| 1096K|00:00:00.09 | 8204 | |
-------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("B"."SLE_NO"="A"."SLE_NO")
3 - filter("A"."SHP_NO"=:V_SHP)
4 - access("A"."SLE_DT">=:D1 AND "A"."SLE_DT"<=:D2)
[이번 주 월요일 · 통계 갱신 뒤]
-------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Used-Mem |
-------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 104 |00:00:00.41 | 18610 | |
| 1 | SORT GROUP BY | | 1 | 104 | 104 |00:00:00.41 | 18610 | 372K (0)|
| 2 | NESTED LOOPS | | 1 | 1890 | 7656 |00:00:00.40 | 18610 | |
| 3 | NESTED LOOPS | | 1 | 1890 | 7656 |00:00:00.22 | 10954 | |
|* 4 | TABLE ACCESS BY INDEX ROWID BATCHED| CF10 | 1 | 863 | 3492 |00:00:00.02 | 478 | |
|* 5 | INDEX RANGE SCAN | IX_CF10_SLE_DT | 1 | 5177 | 20953 |00:00:00.01 | 60 | |
|* 6 | INDEX RANGE SCAN | CF11_PK | 3492 | 2 | 7656 |00:00:00.15 | 10476 | |
| 7 | TABLE ACCESS BY INDEX ROWID | CF11 | 7656 | 2 | 7656 |00:00:00.17 | 7656 | |
-------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("A"."SHP_NO"=:V_SHP)
5 - access("A"."SLE_DT">=:D1 AND "A"."SLE_DT"<=:D2)
6 - access("B"."SLE_NO"="A"."SLE_NO")
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1
---------------------------------------------------------------------------
1 - SEL$1
- LEADING(a b)
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('19.1.0')
DB_VERSION('19.1.0')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
INDEX_RS_ASC(@"SEL$1" "A"@"SEL$1" ("CF10"."SLE_DT"))
BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$1" "A"@"SEL$1")
INDEX(@"SEL$1" "B"@"SEL$1" ("CF11"."SLE_NO" "CF11"."ITM_NO"))
LEADING(@"SEL$1" "A"@"SEL$1" "B"@"SEL$1")
USE_NL(@"SEL$1" "B"@"SEL$1")
NLJ_BATCHING(@"SEL$1" "B"@"SEL$1")
END_OUTLINE_DATA
*/
E-Rows와 CF11의 통계로 쓰시오.USE_HASH(b) 하나만 더하면 왜 모자란지도 쓰시오.