테마
야간 배치 「지역별 주문 파일」은 주문 10만 건을 읽어 물류사에 보낼 파일을 한 줄씩 쓴다. 줄마다 고객의 지역명을 함수 FN_RGN_NM으로 붙인다. 지난주 개발자가 이 함수에 DETERMINISTIC을 붙였다. 고객은 2,000명뿐이니 같은 고객의 지역명은 한 번만 계산될 것이라고 봤다.
개발자가 SQL로 시험했을 때(SELECT COUNT(FN_RGN_NM(CUS_ID)) FROM T_ORD)는 함수 본문이 100,000번에서 57,671번으로 줄었다. 그런데 배치의 경과 시간은 1.6초 그대로이고, 함수 본문도 10만 번 그대로 돈다.
T_ORD 주문: 100,000행 · 896블록 · 고객 2,000명이 돌아가며 섞여 있다(고객 한 명에 약 50건)M_CUS 고객: 2,000행 · 30블록 · PK_M_CUS (CUS_ID) · RGN_IDM_RGN 지역: 8행 · 4블록 · PK_M_RGN (RGN_ID)UTL_FILE)는 배치 시간 중 약 0.3초를 쓴다 · 주문의 고객은 모두 M_CUS에 있다 · 통계 최신CREATE OR REPLACE FUNCTION FN_RGN_NM(p_cus_id VARCHAR2) RETURN VARCHAR2 DETERMINISTIC IS -- 지난주 DETERMINISTIC 추가
v_nm M_RGN.RGN_NM%TYPE;
BEGIN
SELECT r.RGN_NM INTO v_nm
FROM M_CUS c, M_RGN r
WHERE c.CUS_ID = p_cus_id
AND r.RGN_ID = c.RGN_ID;
RETURN v_nm;
END;
/
PROCEDURE P_ORD_FILE(p_dir VARCHAR2) IS
f UTL_FILE.FILE_TYPE;
v_rgn M_RGN.RGN_NM%TYPE;
BEGIN
f := UTL_FILE.FOPEN(p_dir, 'ord_rgn.csv', 'w');
FOR r IN (SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_AMT FROM T_ORD) LOOP
v_rgn := FN_RGN_NM(r.CUS_ID);
UTL_FILE.PUT_LINE(f, r.ORD_SEQ || ',' || r.CUS_ID || ',' || v_rgn || ',' ||
TO_CHAR(r.ORD_DT, 'YYYYMMDDHH24MISS') || ',' || r.ORD_AMT);
END LOOP;
UTL_FILE.FCLOSE(f);
END;
[세션 통계: P_ORD_FILE 한 번]
판 경과 recursive calls session logical reads 함수 본문 실행
DETERMINISTIC 전 1.63 초 101,017 501,913 100,000
DETERMINISTIC 후 1.62 초 101,017 501,911 100,000
[TKPROF 모양: 재귀 문장 둘]
SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_AMT FROM T_ORD
Execute 1 · Fetch 1,001 · rows 100,000 · query 1,897
SELECT R.RGN_NM FROM M_CUS C, M_RGN R WHERE C.CUS_ID = :B1 AND R.RGN_ID = C.RGN_ID
Execute 100,000 · Fetch 100,000 · rows 100,000 · query 500,000 (한 번에 5블록)
[개발자의 시험: SQL 한 문장 (한 번의 페치)]
SELECT COUNT(FN_RGN_NM(CUS_ID)) FROM T_ORD 함수 본문 100,000 → 57,671
DETERMINISTIC은 어떻게 할지도 쓰시오.