Skip to content
튜닝 연습소트 튜닝과 페이징
SORT-003

연말 감사 선물 대상 스무 명을 뽑는 화면이 주문 테이블을 3,340번 찾아간다

기초SELECT한빛유통308 / 700

상황 ​

마케팅팀의 「연말 감사 선물 대상」 확인 화면은 "A 등급 고객 중 올해(2024년) 구매 완료 주문이 한 번이라도 있는 고객"을 뽑는다. 대상을 확정하기 전까지 하루에 수십 번 돌린다. 개발자는 '한 번이라도 있는'을 HAVING COUNT(*) >= 1로 옮겼다.

테이블과 인덱스 ​

  • M_CUS: 2,000행 · 30블록 · CUS_GD A 1%(20명)
  • T_ORD_BIG: 1,000,000행 · 5,826블록 · 고객당 약 500건 · 2024년이 33%(고객당 약 167건) · ORD_ST COMPLETE 90%
  • 인덱스 IX_ORD_BIG_CUS_DT (CUS_ID, ORD_DT)가 있다(CS 화면용). 한 고객의 주문은 테이블 곳곳의 블록에 흩어져 있다. 통계는 최신이다.

원본 SQL ​

sql
SELECT c.CUS_ID, c.CUS_NM, c.CUS_GD
  FROM M_CUS c, T_ORD_BIG o
 WHERE c.CUS_GD = 'A'
   AND o.CUS_ID = c.CUS_ID
   AND o.ORD_DT >= DATE '2024-01-01'
   AND o.ORD_DT <  DATE '2025-01-01'
   AND o.ORD_ST = 'COMPLETE'
 GROUP BY c.CUS_ID, c.CUS_NM, c.CUS_GD
HAVING COUNT(*) >= 1
 ORDER BY c.CUS_ID

실행계획 학습용 예시 ​

xplan
---------------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name              | Starts | E-Rows | A-Rows |    A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                   |      1 |        |     20 | 00:00:00.03 |    3443 |
|*  1 |  SORT GROUP BY                 |                   |      1 |     20 |     20 | 00:00:00.03 |    3443 |
|   2 |   NESTED LOOPS                 |                   |      1 |   3006 |   3006 | 00:00:00.03 |    3443 |
|   3 |    NESTED LOOPS                |                   |      1 |   3340 |   3340 | 00:00:00.01 |     103 |
|*  4 |     TABLE ACCESS FULL          | M_CUS             |      1 |     20 |     20 | 00:00:00.01 |      30 |
|*  5 |     INDEX RANGE SCAN           | IX_ORD_BIG_CUS_DT |     20 |    167 |   3340 | 00:00:00.01 |      73 |
|*  6 |    TABLE ACCESS BY INDEX ROWID | T_ORD_BIG         |   3340 |      1 |   3006 | 00:00:00.02 |    3340 |
---------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(COUNT(*)>=1)
   4 - filter("C"."CUS_GD"='A')
   5 - access("O"."CUS_ID"="C"."CUS_ID" AND "O"."ORD_DT">=TO_DATE(' 2024-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
              "O"."ORD_DT"<TO_DATE(' 2025-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
   6 - filter("O"."ORD_ST"='COMPLETE')

문제 ​

  1. 결과는 20명인데 실행계획이 주문을 몇 건 찾고 몇 블록을 읽었는지 쓰고, 그 일이 왜 필요 없는지 설명하시오.
  2. 결과가 바뀌지 않게 SQL을 고치고, 고친 SQL이 고객마다 '어디서 멈추는지'를 설명하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자