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

조건이 여섯 칸인 정산 검색 프로시저가 어떤 조건으로 찾든 정산 10만 건을 다 읽는다

실전PLSQL한빛유통62 / 700

상황 ​

정산팀 「정산 검색」 화면은 저장 프로시저 P_STLM_SEARCH가 돌려주는 REF CURSOR를 보여 준다. 조건은 정산일 시작과 끝, 고객번호, 주문번호, 정산번호, 금액 하한의 여섯 칸이고 모두 옵션이다. 하루 약 8천 번 호출된다. 2년 전 조민우가 "SQL은 하나로"라는 원칙에 따라 짰다. 한 번에 640블록이라 하루 500만 블록이 넘는다. 박팀장은 ① 지금처럼 정적 SQL 하나 ② 입력 조합마다 정적 SQL을 나누는 분기 ③ 들어온 조건만 붙이는 동적 SQL을 비교해 오라고 했다.

테이블과 인덱스 ​

  • T_ORD_STLM 정산: 100,000행 · 640블록 · 정산일 순으로 쌓였다 · STLM_SEQ · ORD_SEQ · CUS_ID(고객 2,000명, 고객당 약 50건) · STLM_DT DATE(시분초 있음, 하루 약 137건) · STLM_AMT
  • 인덱스 IX_STLM_SEQ (STLM_SEQ) 유니크 · IX_STLM_ORD (ORD_SEQ) · IX_STLM_DT (STLM_DT) · IX_STLM_CUS_DT (CUS_ID, STLM_DT) · 통계 최신
  • 한 달 호출 로그: 고객번호 55% · 주문번호 25% · 정산번호 10% · 정산일 범위만 8% · 금액 하한을 함께 넣은 것 2%
  • 화면은 정산일의 기본값으로 최근 7일을 넣지만 상담원이 지울 수 있다

원본 SQL ​

sql
PROCEDURE P_STLM_SEARCH (
  p_from    IN VARCHAR2,          -- 'YYYYMMDD'
  p_to      IN VARCHAR2,
  p_cus     IN VARCHAR2,
  p_ord     IN NUMBER,
  p_stlm    IN NUMBER,
  p_min_amt IN NUMBER,
  p_rc      OUT SYS_REFCURSOR) IS
BEGIN
  OPEN p_rc FOR
    SELECT STLM_SEQ, ORD_SEQ, CUS_ID, STLM_DT, STLM_AMT
      FROM T_ORD_STLM
     WHERE (p_from    IS NULL OR STLM_DT >= TO_DATE(p_from, 'YYYYMMDD'))
       AND (p_to      IS NULL OR STLM_DT <  TO_DATE(p_to, 'YYYYMMDD') + 1)
       AND (p_cus     IS NULL OR CUS_ID   = p_cus)
       AND (p_ord     IS NULL OR ORD_SEQ  = p_ord)
       AND (p_stlm    IS NULL OR STLM_SEQ = p_stlm)
       AND (p_min_amt IS NULL OR STLM_AMT >= p_min_amt)
     ORDER BY STLM_DT DESC;
END;

실행계획 학습용 예시 ​

xplan
[고객번호 CUS_000123만 넣었을 때(정산일은 지움) · 다른 조합도 계획은 같다]
---------------------------------------------------------------------------------------------
| Id  | Operation           | Name       | Starts | E-Rows | A-Rows | A-Time      | Buffers |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |            |      1 |        |     50 | 00:00:00.01 |     640 |
|   1 |  SORT ORDER BY      |            |      1 |      1 |     50 | 00:00:00.01 |     640 |
|*  2 |   TABLE ACCESS FULL | T_ORD_STLM |      1 |      1 |     50 | 00:00:00.01 |     640 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(((:B6 IS NULL OR "STLM_DT">=TO_DATE(:B6,'YYYYMMDD')) AND (:B5 IS NULL OR
              "STLM_DT"<TO_DATE(:B5,'YYYYMMDD')+1) AND (:B4 IS NULL OR "CUS_ID"=:B4) AND (:B3 IS NULL OR
              "ORD_SEQ"=:B3) AND (:B2 IS NULL OR "STLM_SEQ"=:B2) AND (:B1 IS NULL OR "STLM_AMT">=:B1)))

문제 ​

  1. 어떤 조건을 넣어도 계획이 같고 640블록을 읽는 이유를 설명하시오.
  2. ① 정적 SQL 하나 ② 입력 조합마다 정적 SQL로 분기 ③ 들어온 조건만 붙이는 동적 SQL을 읽는 양, 커서 수, 유지보수, 안전성(SQL 주입, 컴파일 검사)의 관점에서 비교하시오.
  3. 채택한 방법으로 프로시저를 고치시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자