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

「VIP 이달 구매 상품」 83줄을 띄우는 데 고객 마스터를 8,355번 찾아간다

기초SELECT한빛유통210 / 700

상황 ​

CRM팀 상담 화면의 「VIP 이달 구매 상품」 탭은 A 등급 고객 20명이 이번 달에 산 상품을 주문일, 고객명, 상품명, 수량, 금액으로 보여 준다. 상담원 40명이 하루 1,500번쯤 연다. 지난달 조민우가 만들었다. 처음 돌려 보니 해시 조인이 나와서 "화면 조회는 NL이 맞다"며 FROM 절에 적은 순서 그대로 LEADING과 USE_NL을 붙였다. 아래는 2024년 6월로 조회한 실행계획이다.

테이블과 인덱스 ​

  • T_ORD: 100,000행 · 896블록 · PK_T_ORD (ORD_SEQ) · ORD_DT 인덱스 없음 · 한 달 주문은 테이블 전체에 흩어져 있다 · 2024년 6월 4,172건, 그중 A 등급 고객의 주문 41건
  • T_ORD_DET: 200,000행 · 896블록 · PK_T_ORD_DET (ORD_SEQ, ORD_DET_NO) BLEVEL 1 · 주문당 1 ~ 3줄(평균 2줄) · 6월 주문의 상세 8,355줄, A 등급 고객 몫 83줄
  • M_ITM: 100행 · 5블록 · PK_M_ITM (ITM_ID)
  • M_CUS: 2,000행 · 30블록 · PK_M_CUS (CUS_ID) · CUS_GD A 20명(1%) · B 180명 · C 1,800명 · CUS_GD에 히스토그램 있음
  • 통계 최신

원본 SQL ​

sql
SELECT /*+ LEADING(o d i c) USE_NL(d i c) */
       o.ORD_DT, c.CUS_NM, i.ITM_NM, d.ORD_QTY, d.ORD_QTY * d.UNT_PRC AS amt
  FROM T_ORD o, T_ORD_DET d, M_ITM i, M_CUS c
 WHERE o.ORD_DT >= :v_from
   AND o.ORD_DT <  :v_to
   AND d.ORD_SEQ = o.ORD_SEQ
   AND i.ITM_ID  = d.ITM_ID
   AND c.CUS_ID  = o.CUS_ID
   AND c.CUS_GD  = 'A'
 ORDER BY o.ORD_DT DESC, c.CUS_NM

실행계획 학습용 예시 ​

xplan
------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                         | Name         | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  Used-Mem |
------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |              |      1 |        |     83 |00:00:00.29 |   46562 |           |
|   1 |  SORT ORDER BY                    |              |      1 |     84 |     83 |00:00:00.29 |   46562 | 13312 (0) |
|   2 |   NESTED LOOPS                    |              |      1 |     84 |     83 |00:00:00.29 |   46562 |           |
|   3 |    NESTED LOOPS                   |              |      1 |   8344 |   8355 |00:00:00.19 |   29848 |           |
|   4 |     NESTED LOOPS                  |              |      1 |   8344 |   8355 |00:00:00.11 |   13138 |           |
|*  5 |      TABLE ACCESS FULL            | T_ORD        |      1 |   4172 |   4172 |00:00:00.01 |     896 |           |
|   6 |      TABLE ACCESS BY INDEX ROWID  | T_ORD_DET    |   4172 |      2 |   8355 |00:00:00.08 |   12242 |           |
|*  7 |       INDEX RANGE SCAN            | PK_T_ORD_DET |   4172 |      2 |   8355 |00:00:00.04 |    8344 |           |
|   8 |     TABLE ACCESS BY INDEX ROWID   | M_ITM        |   8355 |      1 |   8355 |00:00:00.05 |   16710 |           |
|*  9 |      INDEX UNIQUE SCAN            | PK_M_ITM     |   8355 |      1 |   8355 |00:00:00.02 |    8355 |           |
|* 10 |    TABLE ACCESS BY INDEX ROWID    | M_CUS        |   8355 |      1 |     83 |00:00:00.08 |   16714 |           |
|* 11 |     INDEX UNIQUE SCAN             | PK_M_CUS     |   8355 |      1 |   8355 |00:00:00.03 |    8359 |           |
------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   5 - filter(("O"."ORD_DT">=:V_FROM AND "O"."ORD_DT"<:V_TO))
   7 - access("D"."ORD_SEQ"="O"."ORD_SEQ")
   9 - access("I"."ITM_ID"="D"."ITM_ID")
  10 - filter("C"."CUS_GD"='A')
  11 - access("C"."CUS_ID"="O"."CUS_ID")

문제 ​

  1. 고객 등급 조건(CUS_GD = 'A')이 실행계획의 몇 번 줄에서 적용되는지 짚고, 46,562블록이 어디서 나오는지 Starts와 Buffers로 설명하시오.
  2. 결과는 그대로 두고 일량이 가장 적도록 조인 순서와 조인 방식을 정해 SQL을 작성하시오. 네 테이블의 순서를 정한 기준도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자