테마
한빛유통 주문 모니터링 패키지 PKG_ORD_MON은 2018년에 강도현이 짰다. 작년에 고객센터용 (CUS_ID, ORD_DT) 인덱스와 미결제 대시보드용 (ORD_ST, ORD_DT) 인덱스가 생겼는데, 이 패키지의 두 SQL은 계획이 그대로다. 소스에는 -- 인덱스 타지 말 것 (2018.05 강도현)이라는 주석과 함께 컬럼에 || ''와 + 0이 붙어 있다.
그해 회의록에는 이렇게 적혀 있다. "고객 인덱스를 타면 B2B 대량 고객에서 느림 → 주문일 인덱스로 유도. 주간 범위를 주문일 인덱스로 읽으면 느림 → 상태 인덱스로 유도." 그때 인덱스는 단일 컬럼 (CUS_ID), (ORD_ST), (ORD_DT) 셋이었다.
T_ORD_BIG: 1,000,000행 · 5,826블록 · 하루 913건이 서로 다른 블록에 흩어져 있다CUS_ID 2,000명 · 고객마다 주문 500건(2019년에 B2B 사업을 접어 대량 고객이 없다) · 고객 CUS_000123의 2024년 4 ~ 6월 주문 41건 · 같은 석 달의 전체 주문 83,083건ORD_ST COMPLETE 90% · PENDING 8%(80,000건) · CANCEL 2% · 2024-06-03 ~ 06-09 주간 6,391건 중 PENDING 731건IX_ORD_BIG_CUS_DT (CUS_ID, ORD_DT) · IX_ORD_BIG_ST_DT (ORD_ST, ORD_DT) · IX_ORD_BIG_DT_AMT (ORD_DT, ORD_AMT): 모두 BLEVEL 2 · ORD_ST에 히스토그램 · 통계 최신(2018년의 단일 컬럼 인덱스는 지금 없다)-- A. 고객 최근 주문 (고객센터 화면 · 하루 약 2만 번)
SELECT ORD_SEQ, ORD_DT, ORD_ST, ORD_AMT
FROM T_ORD_BIG
WHERE CUS_ID || '' = :v_cus -- 인덱스 타지 말 것 (2018.05 강도현)
AND ORD_DT >= TO_DATE(:v_from, 'YYYYMMDD')
AND ORD_DT < TO_DATE(:v_to, 'YYYYMMDD') + 1
ORDER BY ORD_DT DESC;
-- B. 미결제 주간 점검 (매시 정각 배치)
SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_AMT
FROM T_ORD_BIG
WHERE ORD_ST = 'PENDING'
AND ORD_DT + 0 >= TO_DATE(:v_from, 'YYYYMMDD') -- 인덱스 타지 말 것 (2018.05 강도현)
AND ORD_DT + 0 < TO_DATE(:v_to, 'YYYYMMDD') + 1;
[A — CUS_000123 · 2024-04-01 ~ 2024-06-30]
----------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 41 |00:00:00.09 | 5826 |
| 1 | SORT ORDER BY | | 1 | 831 | 41 |00:00:00.09 | 5826 |
|* 2 | FILTER | | 1 | | 41 |00:00:00.09 | 5826 |
|* 3 | TABLE ACCESS FULL| T_ORD_BIG | 1 | 831 | 41 |00:00:00.09 | 5826 |
----------------------------------------------------------------------------------------
2 - filter(TO_DATE(:V_TO,'YYYYMMDD')+1>TO_DATE(:V_FROM,'YYYYMMDD'))
3 - filter(("CUS_ID"||''=:V_CUS AND "ORD_DT">=TO_DATE(:V_FROM,'YYYYMMDD') AND
"ORD_DT"<TO_DATE(:V_TO,'YYYYMMDD')+1))
[B — 2024-06-03 ~ 2024-06-09]
-----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 731 |00:00:00.02 | 1046 |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED | T_ORD_BIG | 1 | 400 | 731 |00:00:00.02 | 1046 |
|* 2 | INDEX RANGE SCAN | IX_ORD_BIG_ST_DT | 1 | 400 | 731 |00:00:00.01 | 315 |
-----------------------------------------------------------------------------------------------------------------
2 - access("ORD_ST"='PENDING')
filter(("ORD_DT"+0>=TO_DATE(:V_FROM,'YYYYMMDD') AND "ORD_DT"+0<TO_DATE(:V_TO,'YYYYMMDD')+1))
|| ''와 + 0이 지금 인덱스에서 각각 무엇을 막고 있는지 실행계획의 access와 filter로 설명하시오. 같은 종류의 가공인데 손해의 크기가 크게 다른 이유도 설명하시오.