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

전월 대비 품목 판매표가 10초 걸리고 수량은 수백 배로 부풀고 시즌 메뉴는 빠진다

중급SELECT유통191 / 700

상황 ​

상품팀은 매달 초 「전월 대비 품목 판매」로 품목마다 지난달과 이번 달 판매 수량을 나란히 놓고 증감을 본다. 한 달에만 팔린 시즌 메뉴도 나와야 해서 개발자가 FULL OUTER JOIN으로 썼다. 첫 실행이 10초 가까이 걸렸고, 쉐이크 수량이 실제의 500배 가까이 나왔으며, 11월에만 판 가을 한정 메뉴 2개와 12월에 새로 낸 윈터 시즌 메뉴 4개가 표에 없다. 아래는 2023년 11월과 12월을 비교한 실행계획이다.

테이블과 인덱스 ​

  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) · SLE_NO는 'YYYYMMDD-NNNN'(앞 8자리가 판매일자)
  • 2023년 11월 44,105줄(86개 품목) · 12월 46,322줄(88개 품목) · 두 달 모두 팔린 품목 84개
  • 품목 하나가 한 달에 평균 500줄 안팎 팔린다 · 통계 최신

원본 SQL ​

sql
SELECT NVL(a.ITM_NO, b.ITM_NO) AS ITM_NO,
       SUM(a.SLE_QTY) AS nov_qty,
       SUM(b.SLE_QTY) AS dec_qty
  FROM CF11 a
  FULL OUTER JOIN CF11 b
    ON b.ITM_NO = a.ITM_NO
 WHERE a.SLE_NO >= '20231101' AND a.SLE_NO < '20231201'
   AND b.SLE_NO >= '20231201' AND b.SLE_NO < '20240101'
 GROUP BY NVL(a.ITM_NO, b.ITM_NO)
 ORDER BY 1

실행계획 학습용 예시 ​

xplan
------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |         |      1 |        |     84 |00:00:09.84 |    1754 |          |
|   1 |  SORT GROUP BY                |         |      1 |     88 |     84 |00:00:09.84 |    1754 | 9216  (0)|
|*  2 |   HASH JOIN                   |         |      1 |     23M|     24M|00:00:04.37 |    1754 | 3612K (0)|
|   3 |    TABLE ACCESS BY INDEX ROWID| CF11    |      1 |  43870 |  44105 |00:00:00.02 |     860 |          |
|*  4 |     INDEX RANGE SCAN          | CF11_PK |      1 |  43870 |  44105 |00:00:00.01 |     174 |          |
|   5 |    TABLE ACCESS BY INDEX ROWID| CF11    |      1 |  45980 |  46322 |00:00:00.02 |     894 |          |
|*  6 |     INDEX RANGE SCAN          | CF11_PK |      1 |  45980 |  46322 |00:00:00.01 |     181 |          |
------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("B"."ITM_NO"="A"."ITM_NO")
   4 - access("A"."SLE_NO">='20231101' AND "A"."SLE_NO"<'20231201')
   6 - access("B"."SLE_NO">='20231201' AND "B"."SLE_NO"<'20240101')

문제 ​

  1. 계획 2번 줄이 FULL OUTER가 아닌 HASH JOIN이 된 이유와, 그 줄의 A-Rows 24M이 어디서 나왔는지 설명하시오.
  2. 시즌 메뉴 6개를 포함한 90개 품목이 실제 수량으로 나오도록 SQL을 고치시오. 달 조건을 ON 절로 옮기는 고침은 왜 틀린지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자