테마
CRM 화면의 「지역 고객 오늘 주문」은 지역 하나를 고르면, 그 지역 고객 가운데 오늘 주문한 고객마다 오늘 주문 건수와 금액을 보여 준다. 2022년에 외주 업체가 튜닝하면서 실행계획의 Outline에서 힌트 한 줄을 옮겨 SQL 맨 앞에 붙였다.
지난주 CRM팀이 "최근 12개월 누적 금액" 칸을 더해 달라고 했고, 개발자가 같은 모양의 인라인 뷰를 하나 더 만들어 FROM 절에 끼웠다. 새 뷰만 따로 돌리면 0.1초 남짓인데 화면 전체는 1.7초가 걸린다. 아래는 2024-06-14, 지역 RGN03으로 조회한 실행계획이다.
T_ORD_BIG: 1,000,000행 · 5,826블록 · 하루 약 913건 · 최근 12개월 333,029행 · 적재 순서가 날짜와 무관하다IX_ORD_BIG_DT (ORD_DT): 리프 2,653블록 · 클러스터링 팩터 약 1,000,000M_CUS: 2,000행 · 30블록 · RGN_ID 8종(지역마다 250명)SELECT /*+ INDEX(@SEL$2 o IX_ORD_BIG_DT) */
c.CUS_ID, c.CUS_NM, t.ord_cnt, t.ord_amt, y.yr_amt
FROM M_CUS c,
(SELECT o.CUS_ID, SUM(o.ORD_AMT) AS yr_amt -- 이번에 더한 뷰
FROM T_ORD_BIG o
WHERE o.ORD_DT >= ADD_MONTHS(:v_dt, -12) + 1
AND o.ORD_DT < :v_dt + 1
GROUP BY o.CUS_ID) y,
(SELECT o.CUS_ID, COUNT(*) AS ord_cnt, SUM(o.ORD_AMT) AS ord_amt
FROM T_ORD_BIG o
WHERE o.ORD_DT >= :v_dt
AND o.ORD_DT < :v_dt + 1
GROUP BY o.CUS_ID) t
WHERE c.RGN_ID = :rgn
AND t.CUS_ID = c.CUS_ID
AND y.CUS_ID = c.CUS_ID
--------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Used-Mem |
--------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 92 |00:00:01.71 | 335K | |
|* 1 | HASH JOIN | | 1 | 91 | 92 |00:00:01.71 | 335K | 1180K (0)|
|* 2 | HASH JOIN | | 1 | 91 | 92 |00:00:00.01 | 947 | 1236K (0)|
|* 3 | TABLE ACCESS FULL | M_CUS | 1 | 250 | 250 |00:00:00.01 | 30 | |
| 4 | VIEW | | 1 | 730 | 731 |00:00:00.01 | 917 | |
| 5 | HASH GROUP BY | | 1 | 730 | 731 |00:00:00.01 | 917 | 1290K (0)|
| 6 | TABLE ACCESS BY INDEX ROWID BATCHED| T_ORD_BIG | 1 | 913 | 913 |00:00:00.01 | 917 | |
|* 7 | INDEX RANGE SCAN | IX_ORD_BIG_DT | 1 | 913 | 913 |00:00:00.01 | 4 | |
| 8 | VIEW | | 1 | 2000 | 2000 |00:00:01.70 | 334K | |
| 9 | HASH GROUP BY | | 1 | 2000 | 2000 |00:00:01.70 | 334K | 1420K (0)|
| 10 | TABLE ACCESS BY INDEX ROWID BATCHED | T_ORD_BIG | 1 | 333K| 333K|00:00:01.55 | 334K | |
|* 11 | INDEX RANGE SCAN | IX_ORD_BIG_DT | 1 | 333K| 333K|00:00:00.09 | 884 | |
--------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("Y"."CUS_ID"="C"."CUS_ID")
2 - access("T"."CUS_ID"="C"."CUS_ID")
3 - filter("C"."RGN_ID"=:RGN)
7 - access("O"."ORD_DT">=:V_DT AND "O"."ORD_DT"<:V_DT+1)
11 - access("O"."ORD_DT">=ADD_MONTHS(:V_DT,-12)+1 AND "O"."ORD_DT"<:V_DT+1)
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1
3 - SEL$1 / C@SEL$1
4 - SEL$3 / T@SEL$1
5 - SEL$3
6 - SEL$3 / O@SEL$3
8 - SEL$2 / Y@SEL$1
9 - SEL$2
10 - SEL$2 / O@SEL$2
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1
---------------------------------------------------------------------------
10 - SEL$2 / "O"@"SEL$2"
- INDEX(@SEL$2 o IX_ORD_BIG_DT)
@SEL$2가 무엇을 가리키는지로 설명하시오. Query Block 칸과 Hint Report를 근거로 쓰시오.