Skip to content
튜닝 연습서브쿼리와 쿼리 변환
SUBQ-063

판매 테이블을 한 번만 읽게 고친 1주, 4주, 13주 그래프에서 4주와 13주 막대가 낮아졌다

중급SELECT유통298 / 700

상황 ​

유통 본사 매장 관리 화면의 「최근 판매 추이」 그래프는 매장마다 최근 1주, 4주, 13주 판매 건수를 막대 세 개로 보여 준다. 점장과 본사 담당자가 하루 수백 번 열고, 한 번에 0.3초가 걸린다. 지난주 조민우가 "판매 테이블을 세 번 읽는다"며 한 번만 읽도록 고쳤는데, 시험 화면에서 4주와 13주 막대가 원래보다 낮게 나와 반영을 미뤘다. 아래는 기준일 2023-12-31(1주 20231225 ~, 4주 20231204 ~, 13주 20231002 ~)로 연 화면이다.

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · SLE_DT VARCHAR2(8) · SHP_NO NUMBER(5) 1001 ~ 1005 · 이 문제에서는 CF10_PK (SLE_NO) 말고는 인덱스가 없다
  • 기준일까지 1주 4,862건 · 4주 19,537건 · 13주 63,688건(매장별로는 그 1/5 안팎) · 다섯 매장 모두 매일 판매가 있다
  • 화면은 세 기간의 시작일과 기준일을 바인드(:v_w1, :v_w4, :v_w13, :v_to)로 넘긴다 · 결과는 15행(3기간 × 5매장)
  • 통계 최신

원본 SQL ​

sql
-- 원래 SQL (운영 중)
SELECT '01W' AS PRD, SHP_NO, COUNT(*) AS SLE_CNT
  FROM CF10
 WHERE SLE_DT BETWEEN :v_w1 AND :v_to
 GROUP BY SHP_NO
UNION ALL
SELECT '04W', SHP_NO, COUNT(*)
  FROM CF10
 WHERE SLE_DT BETWEEN :v_w4 AND :v_to
 GROUP BY SHP_NO
UNION ALL
SELECT '13W', SHP_NO, COUNT(*)
  FROM CF10
 WHERE SLE_DT BETWEEN :v_w13 AND :v_to
 GROUP BY SHP_NO
 ORDER BY 1, 2;

-- 조민우가 고친 SQL (4주와 13주가 작게 나온다)
SELECT CASE WHEN SLE_DT >= :v_w1 THEN '01W'
            WHEN SLE_DT >= :v_w4 THEN '04W'
            ELSE '13W' END AS PRD,
       SHP_NO, COUNT(*) AS SLE_CNT
  FROM CF10
 WHERE SLE_DT BETWEEN :v_w13 AND :v_to
 GROUP BY CASE WHEN SLE_DT >= :v_w1 THEN '01W'
               WHEN SLE_DT >= :v_w4 THEN '04W'
               ELSE '13W' END, SHP_NO
 ORDER BY 1, 2;

실행계획 학습용 예시 ​

xplan
[원래 SQL]
------------------------------------------------------------------------------------------
| Id  | Operation              | Name | Starts | E-Rows | A-Rows |      A-Time | Buffers |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |      |      1 |        |     15 | 00:00:00.29 |   13749 |
|   1 |  SORT ORDER BY         |      |      1 |     15 |     15 | 00:00:00.29 |   13749 |
|   2 |   UNION-ALL            |      |      1 |        |     15 | 00:00:00.29 |   13749 |
|   3 |    HASH GROUP BY       |      |      1 |      5 |      5 | 00:00:00.09 |    4583 |
|*  4 |     TABLE ACCESS FULL  | CF10 |      1 |   4795 |   4862 | 00:00:00.09 |    4583 |
|   5 |    HASH GROUP BY       |      |      1 |      5 |      5 | 00:00:00.10 |    4583 |
|*  6 |     TABLE ACCESS FULL  | CF10 |      1 |  19180 |  19537 | 00:00:00.10 |    4583 |
|   7 |    HASH GROUP BY       |      |      1 |      5 |      5 | 00:00:00.10 |    4583 |
|*  8 |     TABLE ACCESS FULL  | CF10 |      1 |  62330 |  63688 | 00:00:00.10 |    4583 |
------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   4 - filter(("SLE_DT">=:V_W1 AND "SLE_DT"<=:V_TO))
   6 - filter(("SLE_DT">=:V_W4 AND "SLE_DT"<=:V_TO))
   8 - filter(("SLE_DT">=:V_W13 AND "SLE_DT"<=:V_TO))

[조민우의 SQL]  TABLE ACCESS FULL 한 번 · Buffers 4,583
  기간별 합계  01W 4,862 · 04W 14,675 · 13W 44,151   (원래 SQL 4,862 · 19,537 · 63,688)

문제 ​

  1. 원래 SQL이 한 번에 읽는 블록 수를 줄 번호로 설명하고, 조민우의 SQL에서 4주와 13주 숫자가 작아진 이유를 설명하시오.
  2. 결과 15행이 원래와 같도록 CF10을 한 번만 읽는 SQL을 작성하시오. 행을 복제하는 시점(집계 전인지 뒤인지)에 따라 일량이 어떻게 다른지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자