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

관심 고객 묶음을 1,000명까지 올릴 수 있게 바꾼 뒤 「묶음 고객 최근 주문」이 읽은 블록 수 상위에 올라왔다

중급SELECT한빛유통231 / 700

상황 ​

상담 화면의 「묶음 고객 최근 주문」은 상담원이 고른 관심 고객 묶음의 고객마다 최근 30일 주문 건수와 금액, 마지막 주문일을 보여 준다. 화면이 고른 고객 번호를 임시 테이블 W_CUS_PICK에 넣은 뒤 아래 SQL을 돌린다. 지금까지 묶음은 30명이 상한이었다. 지난주부터 엑셀로 고객을 1,000명까지 올릴 수 있게 바뀌었고, 그 뒤 이 SQL이 읽은 블록 수 상위에 올라왔다. 오과장이 묶음 크기별로 지금 힌트 판과 해시 판을 재어 왔다. 박팀장은 "힌트를 뗄지 둘지, 언제 다시 볼지 숫자로 정하라"라고 했다.

테이블과 인덱스 ​

  • W_CUS_PICK: 세션별 임시 테이블(GLOBAL TEMPORARY) · CUS_ID 한 칸 · 통계 없음(동적 샘플링으로 건수를 본다) · 묶음 크기 1 ~ 1,000명
  • T_ORD_BIG: 1,000,000행 · 5,826블록 · 고객당 500건 · 최근 30일은 고객당 약 13.7건(전체 27,397건)
  • 인덱스 IX_ORD_BIG_CUS_DT (CUS_ID, ORD_DT) · T_ORD_BIG은 적재 순서가 고객과 무관하다(한 고객의 주문이 여러 블록에 흩어져 있다)
  • 화면은 묶음을 바꿀 때마다 같은 SQL 문장을 다시 실행한다(커서를 공유한다)

원본 SQL ​

sql
SELECT /*+ LEADING(w o) USE_NL(o) INDEX(o IX_ORD_BIG_CUS_DT) */
       o.CUS_ID, COUNT(*) AS ord_cnt, SUM(o.ORD_AMT) AS ord_amt, MAX(o.ORD_DT) AS last_dt
  FROM W_CUS_PICK w, T_ORD_BIG o
 WHERE o.CUS_ID = w.CUS_ID
   AND o.ORD_DT >= :v_from
 GROUP BY o.CUS_ID

실행계획 학습용 예시 ​

xplan
[측정표 · 같은 날 묶음 크기별 Buffers (오과장)]
 묶음 크기 N            10        30       100       300     1,000
 지금 힌트 (NL)        168       502     1,671     5,012    16,703
 해시 (FULL)         5,827     5,827     5,827     5,828     5,829

[지금 · N = 1,000]
-------------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
-------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                   |      1 |        |   1000 |00:00:00.21 |   16703 |          |
|   1 |  HASH GROUP BY                |                   |      1 |     29 |   1000 |00:00:00.21 |   16703 | 1380K (0)|
|   2 |   NESTED LOOPS                |                   |      1 |    397 |  13700 |00:00:00.20 |   16703 |          |
|   3 |    NESTED LOOPS               |                   |      1 |    397 |  13700 |00:00:00.04 |    3003 |          |
|   4 |     TABLE ACCESS FULL         | W_CUS_PICK        |      1 |     29 |   1000 |00:00:00.01 |       3 |          |
|*  5 |     INDEX RANGE SCAN          | IX_ORD_BIG_CUS_DT |   1000 |     14 |  13700 |00:00:00.03 |    3000 |          |
|   6 |    TABLE ACCESS BY INDEX ROWID| T_ORD_BIG         |  13700 |     14 |  13700 |00:00:00.15 |   13700 |          |
-------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access("O"."CUS_ID"="W"."CUS_ID" AND "O"."ORD_DT">=:V_FROM)

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)

문제 ​

  1. 측정표로 NL과 해시의 비용식을 세우고, 손익분기점이 되는 묶음 크기를 구하시오. 지금 힌트가 몇 명까지 맞는지도 쓰시오.
  2. 묶음이 1명이든 1,000명이든 맞는 계획이 돌도록 SQL을 고치시오. 힌트를 떼기만 하면 왜 모자란지 실행계획의 E-Rows로 설명하시오.
  3. 힌트 유효기간 대장에 남길 다음 재검토 조건을 숫자로 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자