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

「신선 상품 월간 판매 상세」가 월초에는 바로 뜨고 월말로 갈수록 느려진다

중급SELECT한빛유통219 / 700

상황 ​

상품팀의 「신선 상품 월간 판매 상세」는 신선 상품 가운데 상품명에 키워드가 든 상품의 이번 달 주문 상세를 보여 준다. 매주 금요일 오후 회의 전에 많이 열린다. 1일 오전에는 0.01초인데 날이 갈수록 느려져서 월말이면 0.5초 가까이 걸린다고 한다. SQL은 바뀐 적이 없다. T_ORD의 통계는 매달 1일 02:00 월초 배치가 모은다. 아래는 2024년 12월 28일 오후에 키워드 '유기농'으로 조회한 실행계획이다.

테이블과 인덱스 ​

  • T_ORD: 2024-12-28 오후 기준 약 99,600행 · 896블록 · PK_T_ORD (ORD_SEQ) · IX_T_ORD_DT (ORD_DT) · ORD_DT는 시분초까지 있어 값이 거의 겹치지 않는다
  • T_ORD 통계: 12월 1일 02:00 수집 · NUM_ROWS 95,800 · ORD_DT 최댓값 2024-11-30 23:58:41 · NDV 95,640
  • 12월 1일부터 28일 오후까지 들어온 주문 3,800건(테이블 전체에 흩어져 있다)
  • T_ORD_DET: 200,000행 · 896블록 · PK_T_ORD_DET (ORD_SEQ, ORD_DET_NO) · 주문당 평균 2줄 / M_ITM: 100행 · 5블록 · ITM_TP 4종(신선 'FRESH' 25개) · 상품명에 '유기농'이 든 신선 상품 8개
  • 12월 주문 가운데 이 상품 8개의 상세는 608줄이다

원본 SQL ​

sql
SELECT o.ORD_SEQ, o.ORD_DT, 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
 WHERE o.ORD_DT >= TRUNC(SYSDATE, 'MM')
   AND d.ORD_SEQ = o.ORD_SEQ
   AND i.ITM_ID  = d.ITM_ID
   AND i.ITM_TP  = 'FRESH'
   AND i.ITM_NM LIKE '%' || :v_kw || '%'
 ORDER BY o.ORD_DT DESC, o.ORD_SEQ

실행계획 학습용 예시 ​

xplan
-------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name         | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  Used-Mem |
-------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |              |      1 |        |    608 |00:00:00.47 |   93701 |           |
|   1 |  SORT ORDER BY                           |              |      1 |      1 |    608 |00:00:00.47 |   93701 | 59392 (0) |
|   2 |   NESTED LOOPS                           |              |      1 |      1 |    608 |00:00:00.47 |   93701 |           |
|   3 |    MERGE JOIN CARTESIAN                  |              |      1 |      1 |  30400 |00:00:00.03 |    2501 |           |
|   4 |     TABLE ACCESS BY INDEX ROWID BATCHED  | T_ORD        |      1 |      1 |   3800 |00:00:00.01 |    2496 |           |
|*  5 |      INDEX RANGE SCAN                    | IX_T_ORD_DT  |      1 |      1 |   3800 |00:00:00.01 |      14 |           |
|   6 |     BUFFER SORT                          |              |   3800 |      1 |  30400 |00:00:00.02 |       5 |  2048 (0) |
|*  7 |      TABLE ACCESS FULL                   | M_ITM        |      1 |      1 |      8 |00:00:00.01 |       5 |           |
|*  8 |    TABLE ACCESS BY INDEX ROWID BATCHED   | T_ORD_DET    |  30400 |      1 |    608 |00:00:00.41 |   91200 |           |
|*  9 |     INDEX RANGE SCAN                     | PK_T_ORD_DET |  30400 |      2 |  60800 |00:00:00.17 |   60800 |           |
-------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access("O"."ORD_DT">=TRUNC(SYSDATE@!,'fmmm'))
   7 - filter(("I"."ITM_TP"='FRESH' AND "I"."ITM_NM" LIKE '%'||:V_KW||'%'))
   8 - filter("D"."ITM_ID"="I"."ITM_ID")
   9 - access("D"."ORD_SEQ"="O"."ORD_SEQ")

문제 ​

  1. 3번 줄 MERGE JOIN CARTESIAN이 왜 골라졌는지 4번 줄과 7번 줄의 E-Rows로 설명하고, 두 E-Rows가 각각 왜 1이 되었는지 쓰시오. 월초에는 빠른 이유도 설명하시오.
  2. 이 계획이 다시 나오지 않도록 고치시오. 고친 뒤의 일량과, 다른 고치는 방법의 대가도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자