Skip to content
튜닝 연습DML과 배치 튜닝
DML-006

월 마감 상품 집계가 상품 수에 비례해 느려진다

기초PLSQL유통431 / 700

상황 ​

본사 레거시 유통의 월 마감 배치에는 「상품별 월 판매 집계」 단계가 있다. 2023년 6월분은 7.6초가 걸렸다. 다음 달에 신상품 40개가 들어온다는 공지가 나오자 배치 담당자가 물었다. "상품이 늘면 집계 시간도 그만큼 느는 건가요?" 소스를 열어 보니 상품 마스터를 한 건씩 돌면서, 상품마다 그 달 판매를 합계 내는 SELECT를 따로 실행하고 있었다.

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · SLE_DT VARCHAR2(8) · 2023년 6월 20,953건
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) · 6월분 45,912행
  • CF01 상품: 109행 · 5블록 · PK ITM_NO · 6월에 팔린 상품은 107개다.
  • 인덱스는 CF10_PK (SLE_NO)와 IX_CF10_SLE_DT (SLE_DT)(03화에서 만든 것)이다. CF11에는 PK 말고 인덱스가 없다.
  • CF11_MON_SUM(집계 결과, 이 문제용 테이블): PK (SLE_YM, ITM_NO) · 배치는 그 달 행을 지우고 다시 넣는다. 통계는 최신이다.

원본 SQL ​

sql
PROCEDURE SUM_ITEM_MONTH(p_ym VARCHAR2) IS      -- p_ym = '202306'
  v_qty NUMBER;
  v_amt NUMBER;
BEGIN
  DELETE FROM CF11_MON_SUM WHERE SLE_YM = p_ym;
  FOR r IN (SELECT ITM_NO FROM CF01) LOOP          -- 상품 109개를 하나씩
    SELECT SUM(b.SLE_QTY), SUM(b.SLE_QTY * b.SLE_UPRC)
      INTO v_qty, v_amt
      FROM CF10 a, CF11 b
     WHERE b.SLE_NO = a.SLE_NO
       AND a.SLE_DT BETWEEN p_ym || '01' AND p_ym || '31'
       AND b.ITM_NO = r.ITM_NO;
    IF v_qty > 0 THEN                              -- 팔린 상품만 넣는다
      INSERT INTO CF11_MON_SUM (SLE_YM, ITM_NO, SLE_QTY, SLE_AMT)
      VALUES (p_ym, r.ITM_NO, v_qty, v_amt);
    END IF;
  END LOOP;
  COMMIT;
END;

실행계획 학습용 예시 ​

xplan
[세션 통계 — SUM_ITEM_MONTH('202306') 한 번]
  경과 시간                 7.4 ~ 7.8 초
  session logical reads     약 908,600
  recursive calls           약 230
  execute count             약 220

[V$SQL — 루프 안의 SELECT]
  SQL_TEXT                                                        EXECUTIONS  BUFFER_GETS  ELAPSED
  SELECT SUM(B.SLE_QTY), SUM(B.SLE_QTY * B.SLE_UPRC) FROM CF10 A …        109      907,970    7.5 초

[그 SELECT 한 번의 실행계획 — DISPLAY_CURSOR (ALLSTATS LAST)]
---------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
---------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                |      1 |        |      1 |00:00:00.07 |    8330 |          |
|   1 |  SORT AGGREGATE                       |                |      1 |      1 |      1 |00:00:00.07 |    8330 |          |
|*  2 |   HASH JOIN                           |                |      1 |    421 |    430 |00:00:00.07 |    8330 | 1824K (0)|
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| CF10           |      1 |  20953 |  20953 |00:00:00.01 |     257 |          |
|*  4 |     INDEX RANGE SCAN                  | IX_CF10_SLE_DT |      1 |  20953 |  20953 |00:00:00.01 |      62 |          |
|*  5 |    TABLE ACCESS FULL                  | CF11           |      1 |  10062 |  10114 |00:00:00.06 |    8073 |          |
---------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("B"."SLE_NO"="A"."SLE_NO")
   4 - access("A"."SLE_DT">=:B1||'01' AND "A"."SLE_DT"<=:B1||'31')
   5 - filter("B"."ITM_NO"=:B2)

문제 ​

  1. 7.6초가 어디서 나오는지 V$SQL과 실행계획으로 설명하고, 상품이 40개 늘면 어떻게 되는지 쓰시오.
  2. 결과가 바뀌지 않게 배치를 고치고, 고친 배치의 일량을 숫자로 적으시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자