테마
본사 레거시 유통의 월 마감에는 「매장 상품 월 판매 요약」 단계가 있다. 지난달까지는 영구 작업 테이블 CF11_WRK_MON을 DELETE로 비우고 다시 채웠는데, 배치 담당자가 "작업 테이블은 임시 테이블로 바꾸면 redo도 줄고 비울 일도 없다"는 말을 듣고 전역 임시 테이블 GT_SLE_MON으로 바꿨다. 사내 배치 표준의 "단계가 끝날 때마다 커밋한다"는 그대로 지켰다.
12월 1일 02:10에 돈 11월분은 0.4초 만에 SUCCEEDED로 끝났다. 그런데 MD팀 화면의 11월 매장 상품 판매가 한 줄도 나오지 않는다. 에러 로그는 비어 있다.
CF10 판매: 500,000행 · 4,583블록 · 2022-01-01 ~ 2023-12-31 · 판매 날짜순 적재 · 2023년 11월 20,712건 · CF10_PK (SLE_NO) · IX_CF10_SLE_DT (SLE_DT)CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) · 11월분 45,359행CF01 상품: 109행 · 5블록 · PK ITM_NO · CTG_NOGT_SLE_MON(이 문제용 전역 임시 테이블): 원본 SQL 맨 위의 DDL로 만들었다. 11월에 팔린 매장 × 상품 조합은 531개다(매장 5곳).CF11_SHP_MON_SUM(요약, 이 문제용): PK (SLE_YM, SHP_NO, ITM_NO) · MD팀 화면이 아침에 읽는다.T_BAT_LOG(배치 기록): JOB_NM · STEP · ROWS_DONE · DONE_AT-- 지난달 바꾼 것: 영구 작업 테이블 CF11_WRK_MON 대신 임시 테이블
CREATE GLOBAL TEMPORARY TABLE GT_SLE_MON (
SHP_NO NUMBER(5),
ITM_NO VARCHAR2(10),
SLE_QTY NUMBER,
SLE_AMT NUMBER
);
PROCEDURE P_SHP_ITM_MON(p_ym VARCHAR2) IS -- p_ym = '202311'
v_rows NUMBER;
BEGIN
-- ⓪ 그 달 요약을 비운다
DELETE FROM CF11_SHP_MON_SUM WHERE SLE_YM = p_ym;
COMMIT; -- 사내 배치 표준: 단계가 끝날 때마다 커밋
-- ① 매장 × 상품 합계를 작업 테이블에
INSERT INTO GT_SLE_MON (SHP_NO, ITM_NO, SLE_QTY, SLE_AMT)
SELECT 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;
v_rows := SQL%ROWCOUNT;
INSERT INTO T_BAT_LOG (JOB_NM, STEP, ROWS_DONE, DONE_AT) VALUES ('SHP_ITM_MON', '1', v_rows, SYSDATE);
COMMIT;
-- ② 상품 분류를 붙여 요약 테이블에
INSERT INTO CF11_SHP_MON_SUM (SLE_YM, SHP_NO, ITM_NO, CTG_NO, SLE_QTY, SLE_AMT)
SELECT p_ym, g.SHP_NO, g.ITM_NO, i.CTG_NO, g.SLE_QTY, g.SLE_AMT
FROM GT_SLE_MON g, CF01 i
WHERE i.ITM_NO = g.ITM_NO;
v_rows := SQL%ROWCOUNT;
INSERT INTO T_BAT_LOG (JOB_NM, STEP, ROWS_DONE, DONE_AT) VALUES ('SHP_ITM_MON', '2', v_rows, SYSDATE);
COMMIT;
END;
[배치 기록 T_BAT_LOG — 12월 1일 · 11월분]
STEP ROWS_DONE DONE_AT
1 531 02:10:03
2 0 02:10:03
스케줄러 상태: SUCCEEDED
[② 문장 — DISPLAY_CURSOR(FORMAT => 'ALLSTATS LAST')]
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
------------------------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | 1 | | 0 |00:00:00.01 | 2 |
| 1 | LOAD TABLE CONVENTIONAL | CF11_SHP_MON_SUM | 1 | | 0 |00:00:00.01 | 2 |
|* 2 | HASH JOIN | | 1 | 1 | 0 |00:00:00.01 | 0 |
| 3 | TABLE ACCESS FULL | GT_SLE_MON | 1 | 1 | 0 |00:00:00.01 | 0 |
| 4 | TABLE ACCESS FULL | CF01 | 0 | 109 | 0 |00:00:00.00 | 0 |
------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("I"."ITM_NO"="G"."ITM_NO")
Note
-----
- dynamic statistics used: dynamic sampling (level=2)
[딕셔너리]
SELECT table_name, temporary, duration FROM user_tables WHERE table_name = 'GT_SLE_MON';
GT_SLE_MON Y SYS$TRANSACTION
DURATION과 원본의 커밋 위치로 설명하시오. 에러 없이 SUCCEEDED로 끝난 이유도 쓰시오.ON COMMIT PRESERVE ROWS로 바꾸는 방법과 비교하고, 실패한 뒤 다시 돌릴 때 어디서부터 돌려야 하는지도 쓰시오.