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

판매 모니터 「최근 판매 20건」이 열 때마다 15만 블록을 읽고, 맨 윗줄은 오전 9시대 판매다

실전SELECT유통323 / 700

상황 ​

본사 판매 모니터는 최근 한 달 판매 가운데 가장 최근 20건을 보여 주고, 줄마다 매장명, 판매상품 수, 판매 금액을 붙인다. 영업지원팀 15명이 수시로 새로 고치는데 열 때마다 0.7초가 걸리고, 매장들에서는 "맨 윗줄이 늘 오전 9시대 판매"라는 민원이 들어온다. 개발자는 화면에 붙는 값을 모두 스칼라 서브쿼리로 붙였고, 정렬은 SLE_DT DESC, SLE_HMS DESC로 했다. SLE_HMS 데이터 교정(앞자리 0 채움)은 승인됐지만 다음 달 정기 점검 때 적용된다.

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · SLE_NO '20231231-0612'(판매일 + 그날 일련번호) · SLE_DT VARCHAR2(8) · SLE_HMS VARCHAR2(6) · 33,241건은 앞자리 0이 빠진 5자리(오전 8 ~ 9시대, '95503') · 하루 약 685건 · CF10_PK (SLE_NO): CF 9,340
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO): BLEVEL 2 · 판매 한 건에 상품 평균 2.2줄
  • CF07 매장: 6행 · 4블록 · PK CF07_PK (SHP_NO) · 판매가 있는 매장은 5곳
  • 교재 09화에서 잰 전제: 판매번호 앞 8자리 = 판매일(500,000 / 500,000), 하루 안에서 판매번호 순서 = 시각 순서(역전 0), 판매번호는 유일(PK)
  • 인덱스 IX_CF10_SLE_DT (SLE_DT)가 있다 · 통계 최신

원본 SQL ​

sql
SELECT *
  FROM (SELECT a.SLE_NO, a.SLE_DT, a.SLE_HMS,
               (SELECT c.SHP_NM FROM CF07 c WHERE c.SHP_NO = a.SHP_NO) AS SHP_NM,
               (SELECT COUNT(*) FROM CF11 b WHERE b.SLE_NO = a.SLE_NO) AS ITM_CNT,
               (SELECT SUM(b.SLE_QTY * b.SLE_UPRC) FROM CF11 b WHERE b.SLE_NO = a.SLE_NO) AS SLE_AMT
          FROM CF10 a
         WHERE a.SLE_DT >= :d_from
         ORDER BY a.SLE_DT DESC, a.SLE_HMS DESC)
 WHERE ROWNUM <= 20

실행계획 학습용 예시 · 데이터 분포는 교재 03화, 09화 실측 ​

xplan
[:d_from = '20231201' · 한 달 21,235건]
-------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
-------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                |      1 |        |     20 |00:00:00.71 |    149K |          |
|*  1 |  COUNT STOPKEY                         |                |      1 |        |     20 |00:00:00.71 |    149K |          |
|   2 |   VIEW                                 |                |      1 |  21235 |     20 |00:00:00.71 |    149K |          |
|   3 |    TABLE ACCESS BY INDEX ROWID         | CF07           |      5 |      1 |      5 |00:00:00.01 |      10 |          |
|*  4 |     INDEX UNIQUE SCAN                  | CF07_PK        |      5 |      1 |      5 |00:00:00.01 |       5 |          |
|   5 |    SORT AGGREGATE                      |                |  21235 |      1 |  21235 |00:00:00.18 |   63705 |          |
|*  6 |     INDEX RANGE SCAN                   | CF11_PK        |  21235 |      2 |  46717 |00:00:00.12 |   63705 |          |
|   7 |    SORT AGGREGATE                      |                |  21235 |      1 |  21235 |00:00:00.27 |   84940 |          |
|   8 |     TABLE ACCESS BY INDEX ROWID BATCHED| CF11           |  21235 |      2 |  46717 |00:00:00.21 |   84940 |          |
|*  9 |      INDEX RANGE SCAN                  | CF11_PK        |  21235 |      2 |  46717 |00:00:00.11 |   63705 |          |
|* 10 |    SORT ORDER BY STOPKEY               |                |      1 |  21235 |     20 |00:00:00.70 |    149K | 4096  (0)|
|  11 |     TABLE ACCESS BY INDEX ROWID BATCHED| CF10           |      1 |  21235 |  21235 |00:00:00.02 |     258 |          |
|* 12 |      INDEX RANGE SCAN                  | IX_CF10_SLE_DT |      1 |  21235 |  21235 |00:00:00.01 |      63 |          |
-------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(ROWNUM<=20)
   4 - access("C"."SHP_NO"=:B1)
   6 - access("B"."SLE_NO"=:B1)
   9 - access("B"."SLE_NO"=:B1)
  10 - filter(ROWNUM<=20)
  12 - access("A"."SLE_DT">=:D_FROM)

[화면 맨 윗줄]  20231231-0037 · 20231231 · 95503 (오전 9:55)   그날 마지막 판매는 20231231-0612 · 225914

문제 ​

  1. 20건을 보여 주는 화면이 15만 블록을 읽는 까닭을 실행계획의 Starts로 설명하고, 맨 윗줄이 오전 9시대 판매인 까닭도 설명하시오.
  2. ㉠ 스칼라 서브쿼리를 Top-N 바깥으로 옮기는 안, ㉡ 정렬 키를 판매번호(SLE_NO DESC)로 바꿔 PK를 거꾸로 읽는 안, ㉢ 데이터 교정을 기다린 뒤 (SLE_DT, SLE_HMS) 순서의 인덱스를 만드는 안을 일량, 정합성, 전제와 부작용으로 비교하시오.
  3. 지금 적용할 SQL을 쓰고, 그 SQL이 기대는 전제를 언제 다시 확인할지 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자