테마
본사 「주간 상품 판매」 리포트는 한 주의 주문을 상품별로 묶는다. 마케팅팀이 월요일마다 지난주를 넣어 열고, 한 번에 0.6초쯤 걸린다. 조민우가 EXPLAIN PLAN을 떠 보고 말했다. "주문 9천 건마다 주문상품 PK를 찾아가는 NL이에요. 해시 조인으로 바꾸게 힌트를 넣죠."
그런데 DBA가 보낸 V$SQL에는 이 SQL이 한 번에 55,523블록을 읽고 대부분이 디스크 읽기라고 나와 있다. NL로 9천 건을 찾아간 일량으로는 보이지 않는다.
ORD: 1,000,000행 · 22,422블록 · ORD_DT VARCHAR2(8) · 하루 약 1,370건 · ORD_X01 (ORD_DT, ORD_HMS)(날짜순 적재, CF 22,223) · ORD_PK (ORD_NO)ORD_ITEM: 2,472,717행 · 55,265블록 · 주문당 약 2.47줄 · ORD_ITEM_PK (ORD_NO, ITEM_ID)ORD는 2013년 7월 1일에 모은 통계 그대로다(NUM_ROWS 749,520 · ORD_DT 최댓값 '20130630'). ORD_ITEM은 최신이다.optimizer_adaptive_plans 기본값(TRUE)-- 조민우 안 : 해시 조인으로 고정한다
SELECT /*+ LEADING(o i) USE_HASH(i) */
i.ITEM_ID, SUM(i.ORD_ITEM_QTY) AS QTY, COUNT(*) AS LINE_CNT
FROM ORD o, ORD_ITEM i
WHERE o.ORD_DT BETWEEN '20131216' AND '20131222'
AND i.ORD_NO = o.ORD_NO
GROUP BY i.ITEM_ID
[조민우의 EXPLAIN PLAN · 힌트 없는 원래 SQL]
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 92 | 6243 |
| 1 | HASH GROUP BY | | 92 | 6243 |
| 2 | NESTED LOOPS | | 3384 | 6231 |
| 3 | NESTED LOOPS | | 3384 | 6231 |
| 4 | TABLE ACCESS BY INDEX ROWID BATCHED| ORD | 1370 | 38 |
|* 5 | INDEX RANGE SCAN | ORD_X01 | 1370 | 7 |
|* 6 | INDEX RANGE SCAN | ORD_ITEM_PK | 2 | 3 |
| 7 | TABLE ACCESS BY INDEX ROWID | ORD_ITEM | 2 | 2 |
-----------------------------------------------------------------------------
Note
-----
- this is an adaptive plan
[DBA가 뜬 실제 실행 · DISPLAY_CURSOR(FORMAT => 'ALLSTATS LAST +ADAPTIVE') · 힌트 없는 원래 SQL]
Plan hash value: 2961807453
-------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |
-------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 87 |00:00:00.61 | 55523 | 55265 |
| 1 | HASH GROUP BY | | 1 | 92 | 87 |00:00:00.61 | 55523 | 55265 |
|* 2 | HASH JOIN | | 1 | 3384 | 23690 |00:00:00.58 | 55523 | 55265 |
|- 3 | NESTED LOOPS | | | 3384 | | | | |
|- 4 | NESTED LOOPS | | | 3384 | | | | |
|- 5 | STATISTICS COLLECTOR | | | | | | | |
| 6 | TABLE ACCESS BY INDEX ROWID BATCHED| ORD | 1 | 1370 | 9590 |00:00:00.01 | 253 | 0 |
|* 7 | INDEX RANGE SCAN | ORD_X01 | 1 | 1370 | 9590 |00:00:00.01 | 38 | 0 |
|-* 8 | INDEX RANGE SCAN | ORD_ITEM_PK | | 2 | | | | |
|- 9 | TABLE ACCESS BY INDEX ROWID | ORD_ITEM | | 2 | | | | |
| 10 | TABLE ACCESS FULL | ORD_ITEM | 1 | 2 | 2472K|00:00:00.35 | 55270 | 55265 |
-------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("I"."ORD_NO"="O"."ORD_NO")
7 - access("O"."ORD_DT">='20131216' AND "O"."ORD_DT"<='20131222')
8 - access("I"."ORD_NO"="O"."ORD_NO")
Note
-----
- this is an adaptive plan (rows marked '-' are inactive)
EXPLAIN PLAN과 실제 실행의 계획이 다른 이유를 설명하시오. 실제로 어느 줄들이 돌았는지, 실행 중에 무엇이 조인 방법을 바꿨는지 쓰시오.USE_HASH 힌트가 필요한지 판단하고, 대신 할 일을 SQL로 쓰시오.