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

누적 실적 배치가 한 번 더 돌자 6월 주문이 두 번 더해졌다

중급MERGE한빛유통433 / 700

상황 ​

매달 2일 새벽에는 지난달 주문을 고객별 누적 실적(누적 건수, 누적 금액, 마지막 주문일)에 더하는 배치가 돈다. 이번 달에는 스케줄러가 응답 없음으로 판단해 배치를 한 번 더 돌렸고(첫 실행도 끝까지 성공했다), 6월분이 두 번 더해졌다. 정산팀 조민우: "VIP 등급 심사가 이 숫자로 나가요." 오과장은 이참에 고객 2,000명을 27,390번 고치는 루프도 같이 고치자고 한다.

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록 · 인덱스 없음 · 2024년 6월 27,390건
  • M_CUS: 2,000행 · 30블록 · PK_M_CUS (CUS_ID). 이 문제에서는 누적 칸 셋(TOT_ORD_CNT, TOT_ORD_AMT, LAST_ORD_DT)이 더 있다. 건수와 금액은 NOT NULL(처음 값 0)이고 LAST_ORD_DT는 NULL일 수 있다.
  • 6월에 주문한 고객은 1,998명이다(한 명에 평균 약 14건).
  • 처리한 달을 적어 두는 테이블은 아직 없다. 만들어도 된다.

원본 SQL ​

sql
PROCEDURE ADD_MONTHLY(p_ym VARCHAR2) IS          -- p_ym = '202406'
BEGIN
  FOR r IN (SELECT CUS_ID, ORD_DT, ORD_AMT
              FROM T_ORD_BIG
             WHERE ORD_DT >= TO_DATE(p_ym, 'YYYYMM')
               AND ORD_DT <  ADD_MONTHS(TO_DATE(p_ym, 'YYYYMM'), 1)) LOOP
    UPDATE M_CUS
       SET TOT_ORD_CNT = TOT_ORD_CNT + 1,
           TOT_ORD_AMT = TOT_ORD_AMT + r.ORD_AMT,
           LAST_ORD_DT = GREATEST(NVL(LAST_ORD_DT, r.ORD_DT), r.ORD_DT)
     WHERE CUS_ID = r.CUS_ID;
  END LOOP;
  COMMIT;
END;

실행계획 학습용 예시 ​

xplan
[세션 통계 — ADD_MONTHLY('202406') 한 번]
  경과 시간                 0.44 ~ 0.48 초
  recursive calls           27,612
  execute count             27,405
  session logical reads     약 88,300
  db block changes          약 55,100
  redo size                 약 8.6 MB
  undo change vector size   약 3.1 MB

[V$SQL — 루프 안의 UPDATE]
  EXECUTIONS 27,390 · ROWS_PROCESSED 27,390 · BUFFER_GETS 82,170 (한 번에 3블록: PK_M_CUS 1 · M_CUS 2)

[스케줄러가 두 번 돌린 뒤 — 6월에 주문한 고객 1,998명]
  TOT_ORD_CNT 가 6월 건수의 두 배만큼 늘어난 고객     1,998
  누적 금액 증가분 / 6월 주문 금액 합계                 정확히 2.00

문제 ​

  1. 이 배치의 일량이 어디서 나오는지 V$SQL과 세션 통계로 설명하시오.
  2. 루프를 한 문장으로 바꾸시오. 6월 주문을 그대로 MERGE … USING에 붙이면 왜 안 되는지도 설명하시오.
  3. 같은 달을 두 번 돌려도 두 번 더해지지 않게 하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자