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

MD 회의 자료의 「매장별 판매 TOP 3」, 같은 12월 데이터인데 뽑을 때마다 3위가 바뀐다

기초SELECT유통363 / 700

상황 ​

본사 MD팀은 월간 회의 자료로 「매장별 판매 수량 TOP 3 상품」을 뽑는다. 12월 회의 전날 오과장이 같은 조건(2023년 12월)으로 두 번 뽑았는데, 1003 매장의 3위가 처음에는 바닐라라떼, 두 번째는 카라멜마키아토였다. 두 상품 모두 655개다. 회의 자료 작성 기준서에는 "수량이 같으면 공동 순위로 모두 싣고, 순위 번호는 1, 2, 2, 4처럼 매긴다"라고 적혀 있다. 개발자는 상위 N건을 뽑을 때 늘 쓰던 ROW_NUMBER를 썼다.

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · SLE_DT VARCHAR2(8) · SHP_NO 판매가 있는 매장 5곳(1001 ~ 1005) · 2023년 12월 21,264건
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) · SLE_QTY / CF01 상품: 109행 · 5블록
  • 인덱스 IX_CF10_SLE_DT (SLE_DT): BLEVEL 2 · 리프 1,393 · CF 9,921 · 통계 최신
  • 2023년 12월 수량(일부): 1003 매장 (ICE)아메리카노 3,412 · 카페라떼 1,206 · 바닐라라떼 655 · 카라멜마키아토 655 · 카페모카 571 / 1005 매장 (ICE)아메리카노 2,980 · 카페라떼 701 · 바닐라라떼 701 · 카페모카 640 · 나머지 세 매장은 4위까지 동점이 없다

원본 SQL ​

sql
SELECT SHP_NO, rn AS RNK, ITM_NO, ITM_NM, QTY
  FROM (SELECT h.SHP_NO, d.ITM_NO, i.ITM_NM, SUM(d.SLE_QTY) AS QTY,
               ROW_NUMBER() OVER (PARTITION BY h.SHP_NO ORDER BY SUM(d.SLE_QTY) DESC) AS rn
          FROM CF10 h, CF11 d, CF01 i
         WHERE h.SLE_DT BETWEEN '20231201' AND '20231231'
           AND d.SLE_NO = h.SLE_NO
           AND i.ITM_NO = d.ITM_NO
         GROUP BY h.SHP_NO, d.ITM_NO, i.ITM_NM)
 WHERE rn <= 3
 ORDER BY SHP_NO, rn

실행계획 학습용 예시 ​

xplan
----------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
----------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |                |      1 |        |     15 |00:00:00.42 |    8693 |          |
|*  1 |  VIEW                                    |                |      1 |    436 |     15 |00:00:00.42 |    8693 |          |
|*  2 |   WINDOW SORT PUSHED RANK                |                |      1 |    436 |     15 |00:00:00.42 |    8693 | 2048  (0)|
|   3 |    HASH GROUP BY                         |                |      1 |    436 |    436 |00:00:00.42 |    8693 | 1520K (0)|
|*  4 |     HASH JOIN                            |                |      1 |  46615 |  46640 |00:00:00.38 |    8693 | 1401K (0)|
|   5 |      TABLE ACCESS FULL                   | CF01           |      1 |    109 |    109 |00:00:00.01 |       5 |          |
|*  6 |      HASH JOIN                           |                |      1 |  46615 |  46640 |00:00:00.35 |    8688 | 1695K (0)|
|   7 |       TABLE ACCESS BY INDEX ROWID BATCHED| CF10           |      1 |  21250 |  21264 |00:00:00.01 |     484 |          |
|*  8 |        INDEX RANGE SCAN                  | IX_CF10_SLE_DT |      1 |  21250 |  21264 |00:00:00.01 |      62 |          |
|   9 |       TABLE ACCESS FULL                  | CF11           |      1 |   1096K|   1096K|00:00:00.12 |    8204 |          |
----------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("RN"<=3)
   2 - filter(ROW_NUMBER() OVER ( PARTITION BY "H"."SHP_NO" ORDER BY SUM("D"."SLE_QTY") DESC )<=3)
   4 - access("I"."ITM_NO"="D"."ITM_NO")
   6 - access("D"."SLE_NO"="H"."SLE_NO")
   8 - access("H"."SLE_DT">='20231201' AND "H"."SLE_DT"<='20231231')

[결과 · 1003 매장 · 같은 조건으로 두 번]
  첫 번째   1 (ICE)아메리카노 3,412 · 2 카페라떼 1,206 · 3 바닐라라떼 655
  두 번째   1 (ICE)아메리카노 3,412 · 2 카페라떼 1,206 · 3 카라멜마키아토 655

문제 ​

  1. 같은 데이터로 두 번 뽑았는데 1003 매장의 3위가 바뀐 까닭을 정렬 키와 ROW_NUMBER의 성질로 설명하시오.
  2. 작성 기준서에 맞게 SQL을 고치고, 1003 매장과 1005 매장에서 ROW_NUMBER, RANK, DENSE_RANK가 각각 몇 줄을 돌려주는지 쓰시오.
  3. 고친 SQL에서도 순위 계산이 매장마다 위의 몇 줄만 남기며 정렬하는지 실행계획으로 확인하고, 읽는 블록 수가 달라지는지 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자