테마
월 마감 프로시저 P_MON_CLOSE는 매월 2일 새벽에 지난달을 마감한다. 월 집계를 쌓고, 미결(PENDING) 주문을 일괄 취소하고, 취소 건수를 점검한다. 원본 T_ORD_BIG에서 월별 파티션 복사본 T_ORD_BIG_P14로 옮기면서 테이블 이름만 바꿨다.
배치 계정은 로그온 트리거로 NLS_DATE_FORMAT = 'YYYY-MM-DD'를 맞춘다. 오과장이 p_ym = '202406'으로 돌린 세 문장의 실행계획을 뽑아 왔다.
T_ORD_BIG_P14 주문(파티션 복사본, 교재 14화): 1,000,000행 · PARTITION BY RANGE (ORD_DT) 월별 36칸과 빈 PMAX, 모두 37칸. P202406은 30번째 칸이고 FULL로 약 161블록, 37칸 전체는 5,868블록이다.ORD_DT는 DATE이고 전부 자정이다. 2024년 6월은 27,390행이고 그중 PENDING이 2,191행, CANCEL이 548행이다.T_MON_SUM 월 집계(문제 설정): 월마다 1행.+ 0은 원본 T_ORD_BIG에 ORD_DT 인덱스가 있던 2019년에 넣었다(소스 주석).PROCEDURE P_MON_CLOSE(p_ym IN VARCHAR2) IS -- p_ym = '202406'
v_from DATE := TO_DATE(p_ym || '01', 'YYYYMMDD');
v_to DATE := ADD_MONTHS(v_from, 1);
v_from_s VARCHAR2(10) := SUBSTR(p_ym, 1, 4) || '-' || SUBSTR(p_ym, 5, 2) || '-01';
v_cnt NUMBER;
BEGIN
-- ① 월 집계
INSERT INTO T_MON_SUM (SUM_MON, ORD_CNT, ORD_AMT)
SELECT v_from, COUNT(*), SUM(ORD_AMT)
FROM T_ORD_BIG_P14
WHERE TO_CHAR(ORD_DT, 'YYYYMM') = p_ym;
-- ② 미결 주문 일괄 취소 (인덱스 타지 말라고 +0 · 2019.05 강도현)
UPDATE T_ORD_BIG_P14
SET ORD_ST = 'CANCEL'
WHERE ORD_ST = 'PENDING'
AND ORD_DT + 0 >= v_from
AND ORD_DT + 0 < v_to;
-- ③ 취소 건수 점검
SELECT COUNT(*) INTO v_cnt
FROM T_ORD_BIG_P14
WHERE ORD_ST = 'CANCEL'
AND ORD_DT >= v_from_s
AND ORD_DT < v_to;
COMMIT;
END;
[① INSERT … SELECT]
----------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Pstart | Pstop |
----------------------------------------------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | 1 | | 0 | 00:00:00.12 | 5873 | | |
| 1 | LOAD TABLE CONVENTIONAL | T_MON_SUM | 1 | | 0 | 00:00:00.12 | 5873 | | |
| 2 | SORT AGGREGATE | | 1 | 1 | 1 | 00:00:00.12 | 5868 | | |
| 3 | PARTITION RANGE ALL | | 1 | 10000 | 27390 | 00:00:00.11 | 5868 | 1 | 37 |
|* 4 | TABLE ACCESS FULL | T_ORD_BIG_P14 | 36 | 10000 | 27390 | 00:00:00.10 | 5868 | 1 | 37 |
----------------------------------------------------------------------------------------------------------------------
4 - filter(TO_CHAR(INTERNAL_FUNCTION("ORD_DT"),'YYYYMM')=:B1)
[② UPDATE]
-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Pstart | Pstop |
-------------------------------------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 1 | | 0 | 00:00:00.16 | 8261 | | |
| 1 | UPDATE | T_ORD_BIG_P14 | 1 | | 0 | 00:00:00.16 | 8261 | | |
| 2 | PARTITION RANGE ALL | | 1 | 200 | 2191 | 00:00:00.11 | 5868 | 1 | 37 |
|* 3 | TABLE ACCESS FULL | T_ORD_BIG_P14 | 36 | 200 | 2191 | 00:00:00.11 | 5868 | 1 | 37 |
-------------------------------------------------------------------------------------------------------------------
3 - filter(("ORD_ST"='PENDING' AND INTERNAL_FUNCTION("ORD_DT")+0>=:B2 AND INTERNAL_FUNCTION("ORD_DT")+0<:B1))
[③ SELECT COUNT(*)]
-------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Pstart | Pstop |
-------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 | 00:00:00.01 | 161 | | |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 | 00:00:00.01 | 161 | | |
|* 2 | FILTER | | 1 | | 2739 | 00:00:00.01 | 161 | | |
| 3 | PARTITION RANGE ITERATOR | | 1 | 548 | 2739 | 00:00:00.01 | 161 | KEY | KEY |
|* 4 | TABLE ACCESS FULL | T_ORD_BIG_P14 | 1 | 548 | 2739 | 00:00:00.01 | 161 | KEY | KEY |
-------------------------------------------------------------------------------------------------------------------------
2 - filter(TO_DATE(:B2)<:B1)
4 - filter(("ORD_ST"='CANCEL' AND "ORD_DT">=TO_DATE(:B2) AND "ORD_DT"<:B1))
[같은 프로시저를 SQL Developer (NLS_DATE_FORMAT = 'DD-MON-RR') 에서 돌리면]
③ ORA-01861: literal does not match format string
+ 0을 지워도 되는지, ③은 칸을 고르는데도 왜 고쳐야 하는지 설명하시오.