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

연습 DB에서는 끝나던 월 집계 배치가 운영에서 ORA-12838로 멈춘다

기초PLSQL유통460 / 700

상황 ​

본사 레거시 유통의 월 마감에 「매장 상품 월 판매 집계」를 새로 만들었다. 매장마다 상품의 판매 금액 순위도 함께 저장한다. 연습 DB(23ai Free)에서 몇 번 돌려 봤을 때는 문제가 없었는데, 12월 1일 운영(19c)에서 처음 돌리자 두 번째 문장에서 ORA-12838로 멈추고 전부 롤백됐다. 배치 담당자: "첫 문장이 끝났으니 그 테이블을 고치는 건 당연히 되는 줄 알았어요."

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · 2023년 11월 20,712건 · IX_CF10_SLE_DT (SLE_DT)
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) · 11월분 45,370행
  • CF11_SHP_MON 매장 상품 월 집계(문제 설정): SLE_YM · SHP_NO · ITM_NO · SLE_QTY · SLE_AMT · AMT_RNK · PK (SLE_YM, SHP_NO, ITM_NO). 매장 다섯 곳에 한 달 약 530행이다. 이 배치만 쓴다.
  • 운영은 Oracle 19c, 연습 DB는 23ai Free다.

원본 SQL ​

sql
PROCEDURE P_SHP_ITM_MON(p_ym VARCHAR2) IS          -- p_ym = '202311'
BEGIN
  -- ① 매장 · 상품별 합계
  INSERT /*+ APPEND */ INTO CF11_SHP_MON (SLE_YM, SHP_NO, ITM_NO, SLE_QTY, SLE_AMT)
  SELECT p_ym, h.SHP_NO, d.ITM_NO, SUM(d.SLE_QTY), SUM(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'
   GROUP BY h.SHP_NO, d.ITM_NO;

  -- ② 매장 안 판매 금액 순위
  UPDATE CF11_SHP_MON m
     SET AMT_RNK = (SELECT COUNT(*) + 1
                      FROM CF11_SHP_MON x
                     WHERE x.SLE_YM = m.SLE_YM
                       AND x.SHP_NO = m.SHP_NO
                       AND x.SLE_AMT > m.SLE_AMT)
   WHERE m.SLE_YM = p_ym;

  COMMIT;
EXCEPTION
  WHEN OTHERS THEN ROLLBACK; RAISE;
END;

실행계획 학습용 예시 ​

xplan
[운영 19c · 12/01 02:10 배치 로그]
  ① INSERT /*+ APPEND */ …   531 rows · 0.21 초 (LOAD AS SELECT)
  ② UPDATE CF11_SHP_MON …
     ORA-12838: cannot read/modify an object after modifying it in parallel
     ORA-06512: at "PUP.P_SHP_ITM_MON", line 12
  → ROLLBACK · CF11_SHP_MON 11월분 0행

[연습 DB 23ai Free · 같은 프로시저 · 같은 데이터]
  ① 531 rows · ② 531 rows updated · COMMIT · 정상 종료

[① 실행계획 — 운영]
----------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT                      |                |      1 |        |      0 |00:00:00.21 |    8462 |
|   1 |  LOAD AS SELECT                       | CF11_SHP_MON   |      1 |        |      0 |00:00:00.21 |    8462 |
|   2 |   HASH GROUP BY                       |                |      1 |    535 |    531 |00:00:00.20 |    8401 |
|*  3 |    HASH JOIN                          |                |      1 |  45614 |  45370 |00:00:00.18 |    8401 |
|   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| CF10          |      1 |  20850 |  20712 |00:00:00.01 |     248 |
|*  5 |      INDEX RANGE SCAN                 | IX_CF10_SLE_DT |      1 |  20850 |  20712 |00:00:00.01 |      59 |
|   6 |     TABLE ACCESS FULL                 | CF11           |      1 |   1096K|   1096K|00:00:00.09 |    8153 |
----------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("D"."SLE_NO"="H"."SLE_NO")
   5 - access("H"."SLE_DT">=:B1||'01' AND "H"."SLE_DT"<=:B1||'31')

문제 ​

  1. ② UPDATE가 운영에서만 ORA-12838로 멈춘 이유를 ① 문장의 적재 방식과 트랜잭션으로 설명하시오.
  2. 같은 결과(합계와 매장 안 순위)를 내도록 프로시저를 고치고, 고친 방법이 나은 이유를 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자