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

발주 수량 환산 배치가 500건째에서 멈췄고, 다시 돌리자 수량이 144배가 됐다

중급UPDATE유통437 / 700

상황 ​

협력사 P007이 포장 단위를 박스(12개)에서 낱개로 바꾸면서, 그 협력사의 진행 중 발주상품 수량을 낱개로 환산(12배)하기로 했다. 개발자는 사내 배치 표준의 "트랜잭션은 짧게"에 따라 500건마다 커밋을 넣었다. 07:10 첫 실행은 ORA-01002로 멈췄고, 07:24에 다시 돌렸는데 같은 자리에서 또 멈췄다. 09:30에 구매팀이 전화했다. "P007 발주 수량이 이상해요. 어떤 건 그대로고 어떤 건 엄청 커요."

테이블과 인덱스 ​

  • CF12 발주: 734행 · 10블록 · PK ORD_NO · P007의 진행 중(ORD_PRGS_CD '10', '20') 발주 139건
  • CF13 발주상품: 7,102행 · 57블록 · PK (ORD_NO, ITM_NO) · 대상 1,384행. 이 문제에서는 단위 칸 ORD_UNIT_CD('BOX' · 'EA')가 더 있고, 대상은 모두 'BOX'다. 배치는 이 칸을 건드리지 않았다.
  • CF13_BAK_0610: 운영 표준에 따라 07:09에 떠 둔 CF13 전체 사본(CTAS)이다. 그 뒤로 대상 행을 고친 것은 이 배치뿐이다(감사 로그로 확인).

원본 SQL ​

sql
DECLARE
  CURSOR c IS
    SELECT d.ORD_QTY
      FROM CF13 d
     WHERE d.ORD_NO IN (SELECT h.ORD_NO FROM CF12 h
                         WHERE h.PTNR_NO = 'P007'
                           AND h.ORD_PRGS_CD IN ('10', '20'))
       FOR UPDATE OF d.ORD_QTY;
  n PLS_INTEGER := 0;
BEGIN
  FOR r IN c LOOP
    UPDATE CF13 SET ORD_QTY = ORD_QTY * 12 WHERE CURRENT OF c;   -- 박스 → 낱개
    n := n + 1;
    IF MOD(n, 500) = 0 THEN COMMIT; END IF;                       -- 트랜잭션은 짧게 (사내 배치 표준)
  END LOOP;
  COMMIT;
END;

실행계획 학습용 예시 ​

xplan
[배치 로그]
  07:10:02  시작 · 대상 1,384행
  07:10:03  500행 처리 · COMMIT
  07:10:03  ORA-01002: fetch out of sequence
  07:24:40  재실행 · 대상 1,384행
  07:24:41  500행 처리 · COMMIT
  07:24:41  ORA-01002: fetch out of sequence

[커서의 실행계획 — 첫 실행 (DISPLAY_CURSOR · ALLSTATS LAST)]
-------------------------------------------------------------------------------------------
| Id  | Operation               | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |      |      1 |        |    500 |00:00:00.01 |    1453 |
|   1 |  FOR UPDATE             |      |      1 |        |    500 |00:00:00.01 |    1453 |
|   2 |   BUFFER SORT           |      |      1 |        |   1384 |00:00:00.01 |      67 |
|*  3 |    HASH JOIN RIGHT SEMI |      |      1 |   1402 |   1384 |00:00:00.01 |      67 |
|*  4 |     TABLE ACCESS FULL   | CF12 |      1 |    141 |    139 |00:00:00.01 |      10 |
|   5 |     TABLE ACCESS FULL   | CF13 |      1 |   7102 |   7102 |00:00:00.01 |      57 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("D"."ORD_NO"="H"."ORD_NO")
   4 - filter(("H"."PTNR_NO"='P007' AND ("H"."ORD_PRGS_CD"='10' OR "H"."ORD_PRGS_CD"='20')))

[09:30 — P007 진행 중 발주상품 1,384행을 사본 CF13_BAK_0610 과 대조]
  ORD_QTY = 사본 × 144     500행
  ORD_QTY = 사본 × 1       884행
  ORD_QTY = 사본 × 12        0행
  ORD_UNIT_CD             1,384행 모두 'BOX'

문제 ​

  1. 배치가 500행째에서 ORA-01002로 멈춘 이유를 커서와 커밋의 관계로 설명하시오.
  2. 다시 돌렸더니 500행이 144배가 된 이유를 설명하시오.
  3. 지금 상태를 바로잡는 SQL과, 앞으로 몇 번을 돌려도 안전한 환산 SQL을 작성하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자