Skip to content
튜닝 연습인덱스 사용 조건
IDX-026

매장 여섯 곳의 일 마감 프로시저가 판매 50만 건을 열두 번 읽고, 마감일은 엉뚱한 모양으로 저장된다

실전PLSQL유통26 / 700

상황 ​

유통 본사의 「일 마감」 프로시저는 매일 새벽 매장마다 전날 판매 건수와 금액을 세어 마감 테이블에 넣는다. 2019년에 작성됐고, 변수 타입은 손으로 적었다. 올해 스케줄러 서버를 바꾼 뒤로 본사 리포트에 지난달 마감이 하루도 나오지 않는다. 리포트는 CLOSE_DT LIKE '202311%'로 마감을 찾는다. 새 스케줄러 서버의 세션은 NLS_LANG=KOREAN_KOREA라서 NLS_DATE_FORMAT이 RR/MM/DD다. 오과장은 원인을 찾는 김에 프로시저의 TKPROF도 떠 왔다.

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록(전체 스캔 4,511) · SLE_DT VARCHAR2(8) · SHP_NO NUMBER(5) · 하루 약 700건(매장마다 약 140건)
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO)
  • CF07 매장: 6행(판매가 있는 매장은 5곳) · SHP_NO NUMBER(5)
  • T_SHP_DAY_CLOSE 일 마감(이 문제에서 쓰는 파생 테이블): CLOSE_DT VARCHAR2(8) ('YYYYMMDD') · SHP_NO NUMBER(5) · SLE_CNT · SLE_AMT · 하루 6행
  • 인덱스 IX_CF10_SLE_DT (SLE_DT): BLEVEL 2 · 리프 1,393 · CF 9,921 · 통계 최신

원본 SQL ​

sql
PROCEDURE P_DAY_CLOSE(p_base_dt IN DATE) IS          -- 스케줄러가 TRUNC(SYSDATE) - 1을 넘긴다
  v_ymd  NUMBER(8) := TO_NUMBER(TO_CHAR(p_base_dt, 'YYYYMMDD'));
  v_cnt  NUMBER;
  v_amt  NUMBER;
BEGIN
  FOR s IN (SELECT TO_CHAR(SHP_NO) AS shp_no FROM CF07) LOOP
    -- ① 매장의 그날 판매 건수
    SELECT COUNT(*) INTO v_cnt
      FROM CF10
     WHERE SLE_DT = v_ymd
       AND SHP_NO = s.shp_no;

    -- ② 매장의 그날 판매 금액
    SELECT NVL(SUM(b.SLE_QTY * b.SLE_UPRC), 0) INTO v_amt
      FROM CF10 a, CF11 b
     WHERE a.SLE_DT = v_ymd
       AND a.SHP_NO = s.shp_no
       AND b.SLE_NO = a.SLE_NO;

    -- ③ 마감 저장
    INSERT INTO T_SHP_DAY_CLOSE (CLOSE_DT, SHP_NO, SLE_CNT, SLE_AMT)
    VALUES (p_base_dt, s.shp_no, v_cnt, v_amt);
  END LOOP;
  COMMIT;
END;

실행계획 학습용 예시 · CF10 규모는 교재 03화 실측 ​

xplan
[TKPROF 요약 · P_DAY_CLOSE(DATE '2023-11-30') 한 번]
① SELECT COUNT(*) FROM CF10 WHERE SLE_DT = :B2 AND SHP_NO = :B1
     Execute 6 · Fetch 6 · query 27,066 · rows 6
② SELECT NVL(SUM(B.SLE_QTY * B.SLE_UPRC), 0) FROM CF10 A, CF11 B WHERE A.SLE_DT = :B2 AND A.SHP_NO = :B1 AND B.SLE_NO = A.SLE_NO
     Execute 6 · Fetch 6 · query 29,886 · rows 6
③ INSERT INTO T_SHP_DAY_CLOSE (CLOSE_DT, SHP_NO, SLE_CNT, SLE_AMT) VALUES (:B4, :B3, :B2, :B1)
     Execute 6 · query 0 · current 18 · rows 6

[① 1번 매장 실행 때 · DBMS_XPLAN.DISPLAY_CURSOR]
-------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      1 |00:00:00.03 |    4511 |
|   1 |  SORT AGGREGATE    |      |      1 |      1 |      1 |00:00:00.03 |    4511 |
|*  2 |   TABLE ACCESS FULL| CF10 |      1 |   1000 |    141 |00:00:00.03 |    4511 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(("SHP_NO"=TO_NUMBER(:B1) AND TO_NUMBER("SLE_DT")=:B2))

[마감 테이블 · 새 서버에서 돈 2023-11-30분]
CLOSE_DT  SHP_NO  SLE_CNT     SLE_AMT
--------  ------  -------  ----------
23/11/30       1      141   1,826,500
23/11/30       2      139   1,794,000
...

문제 ​

  1. ①, ②, ③에서 일어나는 묵시적 형변환을 모두 찾아, 각각 컬럼 쪽에서 일어나는지 변수 쪽에서 일어나는지, 그래서 성능이나 데이터에 무슨 일이 생기는지 설명하시오.
  2. 같은 결과를 내면서 컬럼 쪽에서 형변환이 일어나지 않도록 프로시저를 고치시오.
  3. 같은 실수를 컴파일 단계에서 잡는 방법과, 이미 잘못 저장된 마감 행의 처리를 제안하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자