Skip to content
튜닝 연습파싱과 바인드 변수
PARSE-027

바인드 변수로 바꾸면 피킹 때문에 위험하다며 날짜를 문자열로 이어 붙인다

기초SELECT유통572 / 700

상황 ​

본사 「일별 판매 건수 추이」 화면은 시작일과 종료일을 골라 날짜마다 판매 건수를 그래프로 보여 준다(하루 2,000번쯤, 기간은 하루 ~ 2년). 지난달 인스턴스의 CURSOR_SHARING = FORCE를 EXACT로 되돌린 뒤로 이 화면이 하드 파싱 상위에 올라왔다. 리뷰에서 바인드 변수를 쓰라고 하자 개발자가 답했다. "바인드로 바꾸면 처음 들어온 값이 계획을 정한다고 들었습니다. 기간이 하루일 때와 2년일 때 계획이 같을 리 없잖아요."

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · SLE_DT VARCHAR2(8) '20230601' · 하루 약 706건 · 약 2년치
  • 인덱스 IX_CF10_SLE_DT (SLE_DT): 리프 1,393블록 · 통계 최신 · 히스토그램 없음
  • CURSOR_SHARING = EXACT · 바인드 피킹 켬

원본 SQL ​

sql
// SalesTrendDao.java : 날짜를 SQL 문자열에 이어 붙인다 (화면이 'YYYYMMDD'를 넘긴다)
String sql = "SELECT SLE_DT, COUNT(*) AS SLE_CNT FROM CF10 "
           + "WHERE SLE_DT BETWEEN '" + from + "' AND '" + to + "' "
           + "GROUP BY SLE_DT ORDER BY SLE_DT";
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);

실행계획 학습용 예시 ​

xplan
[같은 모양의 문장을 기간별로 (리터럴 · 실측)]
 기간     A-Rows    계획                                             Buffers
 하루         706   INDEX RANGE SCAN IX_CF10_SLE_DT + SORT GROUP BY NOSORT      4
 31일      21,890   〃                                                  63
 1년      257,700   〃                                                 720
 2년 전체  500,000   〃 (INDEX FAST FULL SCAN + HASH GROUP BY도 비슷)     1,395
 (테이블에는 어느 기간이든 가지 않는다 · FULL이면 4,583)

[V$SQLAREA — 어제 하루 · FORCE_MATCHING_SIGNATURE 하나로 묶은 무리]
  PARENTS 1,310 · EXECS 2,140 · 하드 파싱 1,310 · SHARABLE_MEM 24.1MB

[31일 한 번]
---------------------------------------------------------------------------------------------
| Id  | Operation             | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |                |      1 |        |     31 |00:00:00.01 |      63 |
|   1 |  SORT GROUP BY NOSORT |                |      1 |     31 |     31 |00:00:00.01 |      63 |
|*  2 |   INDEX RANGE SCAN    | IX_CF10_SLE_DT |      1 |  21886 |  21890 |00:00:00.01 |      63 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("SLE_DT">='20230601' AND "SLE_DT"<='20230701')

문제 ​

  1. 개발자의 걱정(첫 값이 계획을 정한다)이 이 문장에서 맞는지 위 측정으로 판단하시오. 바인드 피킹이 해를 끼치려면 무엇이 성립해야 하는지 함께 쓰시오.
  2. 코드를 고치고, 고친 문장이 앞으로도 안전하려면 무엇을 지켜야 하는지 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자