Skip to content
튜닝 연습PL/SQL과 사용자 정의 함수
PLSQL-025

주문 10만 건을 다시 대사하는 데 recursive calls가 40만 번이다

실전PLSQL한빛유통675 / 700

상황 ​

한빛유통은 매월 1일 PG사가 보내는 정정 파일을 받으면 지난 결제 전체(주문 10만 건)를 다시 대사한다. 대사 규칙(결제 수단별 허용 오차, 등급별 예외)은 결제 화면과 같은 패키지 PKG_PAY_RULE에 있고, 결제팀이 매달 고친다. 지난달에는 규칙 안에 PG사 공지 테이블을 읽는 줄이 들어갔다가 빠졌다. 결과는 대사 결과 테이블에 적고, 맞지 않는 주문은 보류(HOLD)로 바꾼다. 박팀장이 TKPROF를 넘기며 말했다. "한 문장으로 다 바꾸자는 말도 나오는데, 규칙을 SQL로 옮기면 결제 화면과 두 벌이 돼요. 어떻게 할지 숫자로 정리해 줘요."

테이블과 인덱스 ​

  • T_ORD 주문: 100,000행 · 896블록 · PK_T_ORD (ORD_SEQ) · PAY_TP · ORD_AMT · PAY_AMT · ORD_ST
  • T_ORD_DET 주문상세: 200,000행 · 896블록 · PK_T_ORD_DET (ORD_SEQ, ORD_DET_NO) · 주문당 1 ~ 3건
  • M_CUS 고객: 2,000행 · 30블록 · PK_M_CUS (CUS_ID) · 모든 주문의 고객이 있다
  • T_PAY_RECON(대사 결과, 이 문제용): RECON_YM · ORD_SEQ · RES_CD · 인덱스 없음 · 회차마다 그 달 것을 지우고 다시 쓴다(이번이 이달 첫 회차)
  • PKG_PAY_RULE.CHECK_PAY: 지금은 SQL 없는 PL/SQL 함수 · 이번 달 결과 'OK' 99,588건 · 'HOLD' 412건
  • 배치가 도는 새벽에는 주문을 고치는 다른 세션이 없다.

원본 SQL ​

sql
PROCEDURE PAY_RECON(p_ym VARCHAR2) IS
  v_gd   M_CUS.CUS_GD%TYPE;
  v_det  NUMBER;
  v_res  VARCHAR2(10);
BEGIN
  DELETE FROM T_PAY_RECON WHERE RECON_YM = p_ym;
  FOR r IN (SELECT ORD_SEQ, CUS_ID, PAY_TP, ORD_AMT, PAY_AMT FROM T_ORD) LOOP
    SELECT CUS_GD INTO v_gd FROM M_CUS WHERE CUS_ID = r.CUS_ID;                  -- ①
    SELECT NVL(SUM(ORD_QTY * UNT_PRC), 0) INTO v_det
      FROM T_ORD_DET WHERE ORD_SEQ = r.ORD_SEQ;                                  -- ②
    v_res := PKG_PAY_RULE.CHECK_PAY(r.PAY_TP, v_gd, r.ORD_AMT, r.PAY_AMT, v_det);
    UPDATE T_ORD
       SET ORD_ST = CASE WHEN v_res = 'OK' THEN ORD_ST ELSE 'HOLD' END
     WHERE ORD_SEQ = r.ORD_SEQ;                                                  -- ③
    INSERT INTO T_PAY_RECON (RECON_YM, ORD_SEQ, RES_CD)
    VALUES (p_ym, r.ORD_SEQ, v_res);                                             -- ④
  END LOOP;
  COMMIT;
END;

실행계획 학습용 예시 ​

xplan
[TKPROF 요약 — PAY_RECON('202412') 한 번 · sys=no]
  문장                                                      Execute     Fetch      rows     query   current   elapsed
  구동 커서 SELECT … FROM T_ORD                                   1     1,001   100,000     1,897         0      0.05
  ① SELECT CUS_GD FROM M_CUS WHERE CUS_ID = :B1              100,000   100,000   100,000   300,000         0      0.41
  ② SELECT NVL(SUM(ORD_QTY*UNT_PRC),0) FROM T_ORD_DET …      100,000   100,000   100,000   300,412         0      0.49
  ③ UPDATE T_ORD SET ORD_ST = CASE … WHERE ORD_SEQ = :B2      100,000         0   100,000   200,000   102,871      1.36
  ④ INSERT INTO T_PAY_RECON … VALUES (:B3, :B2, :B1)         100,000         0   100,000       211   102,946      0.88
  (DELETE는 이달 첫 회차라 0행)

[세션 통계 — 한 번]
  경과 시간 4.6 초 · CPU 4.2 초 · recursive calls 401,038 · session logical reads 1,008,749
  redo size 54.6 MB · undo change vector size 17.9 MB
  결과: OK 99,588건 · HOLD 412건

문제 ​

  1. 일량이 어디서 나오는지 TKPROF의 문장별로 나누어 설명하시오. 그 가운데 엔진 왕복이 아니라 하지 않아도 되는 일인 것도 찾으시오.
  2. 규칙 함수는 그대로 두고 배치를 다시 작성하시오.
  3. 함수를 SQL 안에서 부르는 한 문장(INSERT … SELECT와 UPDATE) 판과 비교해 이 판을 고른 근거와, 고친 판의 부작용을 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자