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

VIP 스무 명의 반년 치 주문 260줄에 2만 6천 블록을 읽는다

중급SELECT한빛유통153 / 700

상황 ​

CRM팀의 「VIP 고객 하반기 주문」은 상담 전에 A 등급 고객들의 기간 주문을 한 번에 뽑는 화면이다. 기간만 바꿔 가며 하루 수백 번 호출된다. 소스의 힌트 옆에는 -- 주문을 먼저 읽고 고객은 PK 로 붙인다 (마스터는 나중에)라는 주석이 있다.

테이블과 인덱스 ​

  • T_ORD: 100,000행 · 896블록 · PK_T_ORD (ORD_SEQ) · IX_T_ORD_CUS_DT (CUS_ID, ORD_DT) (작년에 고객 상담 화면용으로 만든 인덱스) · ORD_DT 시분초 있음 · 2023-01-01 ~ 2024-12-31에 고르게 분포 · 고객마다 50건
  • M_CUS: 2,000행 · 30블록 · PK_M_CUS (CUS_ID) · CUS_GD A 1% (20명) · B 9% · C 90%
  • 통계 최신 · 히스토그램 없음 (FOR ALL COLUMNS SIZE 1) · 아래 실행계획은 2024-07-01 ~ 2024-12-31 (주문 25,062건)

원본 SQL ​

sql
SELECT /*+ LEADING(o c) USE_NL(c) */
       c.CUS_ID, c.CUS_NM, o.ORD_SEQ, o.ORD_DT, o.ORD_AMT
  FROM T_ORD o, M_CUS c
 WHERE o.ORD_DT >= TO_DATE(:v_from, 'YYYYMMDD')
   AND o.ORD_DT <  TO_DATE(:v_to, 'YYYYMMDD') + 1
   AND c.CUS_ID = o.CUS_ID
   AND c.CUS_GD = 'A'
 ORDER BY c.CUS_ID, o.ORD_DT

실행계획 학습용 예시 ​

xplan
---------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |          |      1 |        |    260 |00:00:00.05 |   26484 |
|   1 |  SORT ORDER BY                 |          |      1 |   8383 |    260 |00:00:00.05 |   26484 |
|*  2 |   FILTER                       |          |      1 |        |    260 |00:00:00.05 |   26484 |
|   3 |    NESTED LOOPS                |          |      1 |   8383 |    260 |00:00:00.05 |   26484 |
|   4 |     NESTED LOOPS               |          |      1 |  25150 |  25062 |00:00:00.02 |    1422 |
|*  5 |      TABLE ACCESS FULL         | T_ORD    |      1 |  25150 |  25062 |00:00:00.01 |     896 |
|*  6 |      INDEX UNIQUE SCAN         | PK_M_CUS |  25062 |      1 |  25062 |00:00:00.01 |     526 |
|*  7 |     TABLE ACCESS BY INDEX ROWID| M_CUS    |  25062 |      1 |    260 |00:00:00.02 |   25062 |
---------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(TO_DATE(:V_TO,'YYYYMMDD')+1>TO_DATE(:V_FROM,'YYYYMMDD'))
   5 - filter(("O"."ORD_DT">=TO_DATE(:V_FROM,'YYYYMMDD') AND "O"."ORD_DT"<TO_DATE(:V_TO,'YYYYMMDD')+1))
   6 - access("C"."CUS_ID"="O"."CUS_ID")
   7 - filter("C"."CUS_GD"='A')

문제 ​

  1. 6번과 7번 줄의 Starts를 7번 줄의 A-Rows와 비교해, 이 조인이 무엇을 낭비하는지 설명하시오.
  2. 두 조건(기간, 등급)의 선택도로 어느 쪽을 먼저 읽어야 하는지 정하고 개선 SQL을 작성하시오. 힌트 없이 옵티마이저가 그 순서를 고르게 하려면 무엇이 필요한지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자