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

협력사 월 납품 실적 보고 체인 여섯 단계가 결과 617행을 만드는 데 12.7초와 redo 35MB를 쓴다

실전INSERT유통493 / 700

상황 ​

본사 레거시 유통의 월 마감에는 외주 개발사가 2018년에 만든 「협력사 월 납품 실적 보고」 체인이 있다. 판매상품을 작업 테이블에 옮기고, 행마다 협력사와 분류를 채우고, 자체 상품을 지우고, 묶고, 이름을 붙여 리포트 테이블에 넣는 여섯 단계이고 단계마다 커밋한다. 결과는 617행이다. 월 마감 창을 줄이는 작업을 맡은 오과장이 물었다. "단계마다 하나씩 튜닝하면 될까요, 아예 다시 짤까요? 다시 짜면 숫자가 지금과 똑같이 나와야 합니다. 협력사들이 이 숫자로 납품 대금을 맞춰 봐요."

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · 판매 날짜순 적재 · 2023년 6월 20,953건 · IX_CF10_SLE_DT (SLE_DT) · SHP_NO 매장 5곳
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) · 6월분 45,912행
  • CF01 상품: 109행 · 5블록 · ITM_NO · CTG_NO · PTNR_NO(협력사, 자체 상품은 'P000') · 6월에 팔린 상품 중 2개는 단종 정리로 마스터에서 지웠다(그 판매상품 87행).
  • CF02 카테고리: 18행 · 4블록 · CF07 매장: 6행 · 4블록
  • W_SLE_1, W_SLE_2: 이 체인만 쓰는 영구 작업 테이블(LOGGING, 인덱스 없음) · RPT_PTNR_MON: 리포트 테이블(이 문제용), 협력사 화면이 읽는다.
  • 운영은 ARCHIVELOG다.

원본 SQL ​

sql
PROCEDURE P_PTNR_MON(p_ym VARCHAR2) IS            -- p_ym = '202306'
  v_ptnr CF01.PTNR_NO%TYPE;
  v_ctg  CF01.CTG_NO%TYPE;
  v_cnm  CF02.CTG_NM%TYPE;
  v_snm  CF07.SHP_NM%TYPE;
BEGIN
  -- ① 그 달 판매상품을 작업 테이블로
  INSERT INTO W_SLE_1 (SLE_NO, SHP_NO, ITM_NO, SLE_QTY, SLE_UPRC)
  SELECT h.SLE_NO, h.SHP_NO, d.ITM_NO, d.SLE_QTY, d.SLE_UPRC
    FROM CF10 h, CF11 d
   WHERE d.SLE_NO = h.SLE_NO
     AND h.SLE_DT BETWEEN p_ym || '01' AND p_ym || '31';
  COMMIT;

  -- ② 행마다 협력사와 분류를 채운다
  FOR r IN (SELECT ROWID AS rid, ITM_NO FROM W_SLE_1) LOOP
    BEGIN
      SELECT PTNR_NO, CTG_NO INTO v_ptnr, v_ctg FROM CF01 WHERE ITM_NO = r.ITM_NO;
    EXCEPTION
      WHEN NO_DATA_FOUND THEN v_ptnr := NULL; v_ctg := NULL;      -- 마스터에 없는 상품은 비워 둔다
    END;
    UPDATE W_SLE_1 SET PTNR_NO = v_ptnr, CTG_NO = v_ctg WHERE ROWID = r.rid;
  END LOOP;
  COMMIT;

  -- ③ 자체 상품은 보고 대상이 아니다
  DELETE FROM W_SLE_1 WHERE PTNR_NO = 'P000';
  COMMIT;

  -- ④ 협력사 · 분류 · 매장으로 묶는다
  INSERT INTO W_SLE_2 (PTNR_NO, CTG_NO, SHP_NO, SLE_QTY, SLE_AMT)
  SELECT PTNR_NO, CTG_NO, SHP_NO, SUM(SLE_QTY), SUM(SLE_QTY * SLE_UPRC)
    FROM W_SLE_1
   GROUP BY PTNR_NO, CTG_NO, SHP_NO;
  COMMIT;

  -- ⑤ 이름을 붙여 리포트에
  FOR r IN (SELECT * FROM W_SLE_2) LOOP
    BEGIN
      SELECT CTG_NM INTO v_cnm FROM CF02 WHERE CTG_NO = r.CTG_NO;
    EXCEPTION
      WHEN NO_DATA_FOUND THEN v_cnm := '(미분류)';
    END;
    SELECT SHP_NM INTO v_snm FROM CF07 WHERE SHP_NO = r.SHP_NO;
    INSERT INTO RPT_PTNR_MON (SLE_YM, PTNR_NO, CTG_NO, CTG_NM, SHP_NO, SHP_NM, SLE_QTY, SLE_AMT)
    VALUES (p_ym, r.PTNR_NO, r.CTG_NO, v_cnm, r.SHP_NO, v_snm, r.SLE_QTY, r.SLE_AMT);
  END LOOP;
  COMMIT;

  -- ⑥ 작업 테이블 비우기
  DELETE FROM W_SLE_1;
  DELETE FROM W_SLE_2;
  COMMIT;
END;

실행계획 학습용 예시 ​

xplan
[단계별 — 2023년 6월분 · 운영 · 단독 실행]
  단계                            처리 행   경과    redo      recursive calls   비고
  ① W_SLE_1 적재                   45,912   0.9초    6.1 MB              12   CF10 · CF11 해시 조인 12,787블록
  ② 협력사 · 분류 채우기             45,912   9.8초   14.2 MB          91,830   행마다 CF01 SELECT INTO + ROWID UPDATE
  ③ 자체 상품 지우기                  6,017   0.4초    2.9 MB               6
  ④ W_SLE_2 집계                       617   0.2초    0.1 MB               4
  ⑤ 리포트 적재                        617   0.3초    0.2 MB           1,858   행마다 CF02 · CF07 SELECT INTO + INSERT
  ⑥ 작업 테이블 비우기               40,512   1.1초   11.6 MB               4   W_SLE_1 39,895 + W_SLE_2 617
  합계                                     12.7초   35.1 MB          93,714   커밋 6번

[6월 리포트에 든 것]
  617행 · 그중 PTNR_NO 가 빈 행 3행 (마스터에 없는 상품 87행이 '(미분류)'로 묶인 것)

문제 ​

  1. 12.7초와 redo 35.1MB가 어디서 나오는지 단계별 표로 설명하고, 작업 테이블 두 개가 각각 몇 번 쓰이고 몇 번 읽히는지 쓰시오.
  2. 체인 전체를 한 문장으로 다시 작성하시오. 결과가 지금과 똑같으려면 ②, ③, ⑤의 어떤 동작을 빠짐없이 옮겨야 하는지 쓰시오.
  3. 작업 테이블을 남기는 편이 나은 경우는 언제인지 쓰고, 바꾸기 전에 결과가 같은지 확인하는 방법을 SQL로 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자