테마
쇼핑몰 관리 화면의 「주문 검색」은 고객번호, 주문 상태, 주문일 시작과 끝 네 칸 중 채운 칸만 조건으로 쓴다. 프로시저 P_ORD_SEARCH가 조건을 이어 붙여 동적 SQL을 만들고 결과를 SYS_REFCURSOR로 돌려준다.
작년에 바인드 변수로 바꾸려다 "채운 칸마다 바인드 개수가 달라 USING을 쓸 수 없다"(ORA-01008)며 되돌렸다. DBA는 이 프로시저의 문장이 하루 2만 개 넘게 하드 파싱되어 공유 풀을 밀어낸다고 한다.
T_ORD 주문: 100,000행 · 896블록 · ORD_DT DATE(시분초 있음, 2023 ~ 2024, 하루 약 137건) · ORD_ST COMPLETE 90% · PENDING 8% · CANCEL 2% · 고객마다 50건PK_T_ORD (ORD_SEQ) · IX_T_ORD_CUS_DT (CUS_ID, ORD_DT) · IX_T_ORD_DT (ORD_DT) · 통계 최신CURSOR_SHARING = EXACTPROCEDURE P_ORD_SEARCH(p_cus_id IN VARCHAR2, p_ord_st IN VARCHAR2,
p_dt_fr IN DATE, p_dt_to IN DATE,
o_rc OUT SYS_REFCURSOR) IS
v_sql VARCHAR2(4000) := 'SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_ST, ORD_AMT FROM T_ORD WHERE 1 = 1';
BEGIN
IF p_cus_id IS NOT NULL THEN
v_sql := v_sql || ' AND CUS_ID = ''' || p_cus_id || '''';
END IF;
IF p_ord_st IS NOT NULL THEN
v_sql := v_sql || ' AND ORD_ST = ''' || p_ord_st || '''';
END IF;
IF p_dt_fr IS NOT NULL THEN
v_sql := v_sql || ' AND ORD_DT >= TO_DATE(''' || TO_CHAR(p_dt_fr, 'YYYYMMDD') || ''', ''YYYYMMDD'')';
END IF;
IF p_dt_to IS NOT NULL THEN
v_sql := v_sql || ' AND ORD_DT < TO_DATE(''' || TO_CHAR(p_dt_to, 'YYYYMMDD') || ''', ''YYYYMMDD'') + 1';
END IF;
OPEN o_rc FOR v_sql || ' ORDER BY ORD_DT DESC';
END;
[V$SQLAREA: P_ORD_SEARCH가 만든 문장 · 하루 (FORCE_MATCHING_SIGNATURE로 묶음)]
채운 칸 문장 수 실행 수 하드 파싱
고객 6,912 9,840 6,912
고객 + 기간 8,105 8,330 8,105
상태 + 기간 3,288 3,420 3,288
기간 2,617 2,790 2,617
그 밖의 조합 다섯 가지 940 990 940
합계 (아홉 가지 조합) 21,862 25,370 21,862
[세션 통계 · 하루 합] parse count (hard) 21,862 · parse time cpu 1,940 cs
[한 번의 실행 · 고객만 채움]
-------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 50 |00:00:00.01 | 53 |
| 1 | TABLE ACCESS BY INDEX ROWID | T_ORD | 1 | 50 | 50 |00:00:00.01 | 53 |
|* 2 | INDEX RANGE SCAN DESCENDING| IX_T_ORD_CUS_DT | 1 | 50 | 50 |00:00:00.01 | 3 |
-------------------------------------------------------------------------------------------------------
2 - access("CUS_ID"='CUS_000123')
(:v IS NULL OR 컬럼 = :v) 모양의 정적 SQL 하나로 쓰는 안과 견주어 쓰시오.