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

판매일 인덱스가 있는데 기간별 판매 전표 조회가 FULL로 돌고 1초 가까이 걸린다

기초SELECT유통678 / 700

상황 ​

본사 회계팀의 「기간별 판매 전표」 화면은 시작일과 종료일을 달력에서 고르고, Java가 두 값을 DATE 바인드(setDate)로 넘긴다. 한 달을 고르면 1초 가까이 걸린다. SLE_DT가 문자 컬럼이라서 개발자는 공통 함수 FN_STR2DT로 판매일을 날짜로 바꿔 비교했다. 개발자의 설명은 "형변환을 직접 했으니 묵시적 형변환 문제는 없다"는 것이다.

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · SLE_DT VARCHAR2(8) '20231201' · 2022 ~ 2023년 · 2023년 12월 20,864건
  • 인덱스 IX_CF10_SLE_DT (SLE_DT): 이 문제에서는 있다 · 리프 1,393블록 · 통계 최신
  • FN_STR2DT: SQL이 없는 PL/SQL 함수 · 날짜로 바꿀 수 없는 문자열이면 NULL을 돌려준다 · 다른 화면 10여 곳도 쓴다
  • 이번 주에 전수 점검한 결과 SLE_DT에는 날짜로 바꿀 수 없는 값이 없다

원본 SQL ​

sql
CREATE OR REPLACE FUNCTION FN_STR2DT(p_ymd VARCHAR2) RETURN DATE IS
BEGIN
  RETURN TO_DATE(p_ymd, 'YYYYMMDD');
EXCEPTION
  WHEN OTHERS THEN RETURN NULL;          -- 잘못된 날짜 문자열은 NULL
END;
/

-- :d_fr = 2023-12-01, :d_to = 2023-12-31 (DATE 바인드)
SELECT h.SLE_NO, h.SLE_DT, h.SLE_HMS, h.SHP_NO
  FROM CF10 h
 WHERE FN_STR2DT(h.SLE_DT) BETWEEN :d_fr AND :d_to;

실행계획 학습용 예시 ​

xplan
[DISPLAY_CURSOR (ALLSTATS LAST) · 2023-12-01 ~ 2023-12-31]
-------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |  20864 |00:00:00.94 |    4583 |
|*  1 |  TABLE ACCESS FULL | CF10 |      1 |   1250 |  20864 |00:00:00.94 |    4583 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(("PUP"."FN_STR2DT"("H"."SLE_DT")>=:D_FR AND "PUP"."FN_STR2DT"("H"."SLE_DT")<=:D_TO))

[V$SQL: 이 문장]         ELAPSED_TIME 0.94 초 · CPU_TIME 0.92 초 · PLSQL_EXEC_TIME 0.71 초 · BUFFER_GETS 4,583
[DBMS_HPROF: FN_STR2DT]  호출 520,864번 (함수 안에 SQL이 없어 recursive calls는 늘지 않는다)

문제 ​

  1. SLE_DT에 인덱스가 있는데도 테이블 전체를 읽는 이유를 Predicate Information으로 설명하시오. 함수가 행 수(500,000)보다 많은 520,864번 호출된 이유도 쓰시오.
  2. 결과가 바뀌지 않게 SQL을 고쳐 인덱스 범위 스캔이 되도록 하시오. 고친 SQL이 원래 SQL과 같은 결과를 내려면 무엇이 전제되어야 하는지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자