Skip to content
튜닝 연습조인 튜닝
JOIN-011

지역 고객 250명의 6월 매출을 내는데 12만 블록을 읽는다

중급SELECT한빛유통161 / 700

상황 ​

영업팀의 「지역별 고객 월 매출」은 지역 하나를 고르면 그 지역 고객마다 한 달 주문 건수와 금액을 낸다. 지역 담당자 8명이 달을 바꿔 가며 하루 400번쯤 돌린다. 작년에 T_ORD_BIG에 고객 번호 인덱스를 만들고 NL 조인으로 바꾼 뒤 "빨라졌다"는 보고가 있었지만, 지금은 한 번에 2초 가까이 걸린다. 아래는 RGN03 지역(고객 250명)의 2024년 6월 실행계획이다.

테이블과 인덱스 ​

  • M_CUS: 2,000행 · 30블록 · PK_M_CUS (CUS_ID) · 지역 8곳에 250명씩
  • T_ORD_BIG: 1,000,000행 · 5,826블록 · 고객마다 500건 · 고객 번호를 돌려 가며 적재해서 한 고객의 주문은 테이블 전체에 흩어져 있다 · 2024년 6월 27,390건
  • 인덱스 IX_ORD_BIG_CUS (CUS_ID): 높이 3 · 리프 약 2,700블록 · 통계 최신
  • RGN03 고객 250명의 6월 주문은 3,423건 (고객마다 12 ~ 15건)

원본 SQL ​

sql
SELECT /*+ LEADING(c o) USE_NL(o) INDEX(o IX_ORD_BIG_CUS) */
       c.CUS_ID, c.CUS_NM, COUNT(*) AS ord_cnt, SUM(o.ORD_AMT) AS ord_amt
  FROM M_CUS c, T_ORD_BIG o
 WHERE c.RGN_ID = :v_rgn
   AND o.CUS_ID = c.CUS_ID
   AND o.ORD_DT >= TO_DATE(:v_ym || '01', 'YYYYMMDD')
   AND o.ORD_DT <  ADD_MONTHS(TO_DATE(:v_ym || '01', 'YYYYMMDD'), 1)
 GROUP BY c.CUS_ID, c.CUS_NM

실행계획 학습용 예시 · 행 수는 실데이터 ​

xplan
-----------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                |      1 |        |    250 |00:00:01.84 |    126K |
|   1 |  HASH GROUP BY                 |                |      1 |    250 |    250 |00:00:01.84 |    126K |
|   2 |   NESTED LOOPS                 |                |      1 |   3425 |   3423 |00:00:01.83 |    126K |
|   3 |    NESTED LOOPS                |                |      1 |   125K |   125K |00:00:00.06 |    1037 |
|*  4 |     TABLE ACCESS FULL          | M_CUS          |      1 |    250 |    250 |00:00:00.01 |      30 |
|*  5 |     INDEX RANGE SCAN           | IX_ORD_BIG_CUS |    250 |    500 |   125K |00:00:00.04 |    1007 |
|*  6 |    TABLE ACCESS BY INDEX ROWID | T_ORD_BIG      |   125K |     14 |   3423 |00:00:01.74 |    125K |
-----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   4 - filter("C"."RGN_ID"=:V_RGN)
   5 - access("O"."CUS_ID"="C"."CUS_ID")
   6 - filter(("O"."ORD_DT">=TO_DATE(:V_YM||'01','YYYYMMDD') AND
              "O"."ORD_DT"<ADD_MONTHS(TO_DATE(:V_YM||'01','YYYYMMDD'),1)))

문제 ​

  1. 6번 줄의 Starts와 A-Rows, Predicate Information 6번을 보고 12만 블록이 어디서 나오는지 설명하시오.
  2. inner 쪽 인덱스를 고쳐 개선안을 작성하고, 해시 조인으로 바꾸는 안과 Buffers를 비교하시오. 인덱스 컬럼 순서를 어떻게 정했는지도 서술하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자