테마
본사 마케팅팀의 「요일별 시간대 주문 수」 리포트는 2년 치 주문 100만 건을 요일과 시간대로 묶는다. 1.7초가 걸리는데, 같은 주문을 일자별로 묶는 리포트는 0.5초다. DBA는 "둘 다 Buffers 22,422로 같은데 왜 세 배 차이가 나는지 모르겠다. recursive calls도 거의 없다"라고 한다. 요일 이름과 시간대 구분은 개발팀 공통 함수 두 개가 만든다. 두 함수 모두 테이블을 읽지 않는다.
ORD 주문: 1,000,000행 · 22,422블록 · ORD_DT VARCHAR2(8) '20120101' ~ '20131231'(731일) · ORD_HMS VARCHAR2(6) 09시 ~ 22시 · 두 컬럼 모두 NULL 허용ORD_X01 (ORD_DT, ORD_HMS) 리프 3,774블록: 두 컬럼이 NULL 허용이라 이 리포트는 테이블을 FULL로 읽는다FN_DOW_NM, FN_HOUR_BAND: PL/SQL 계산만 하는 함수(SQL 없음) · 통계 최신CREATE OR REPLACE FUNCTION FN_DOW_NM(p_ymd VARCHAR2) RETURN VARCHAR2 IS
BEGIN
RETURN TO_CHAR(TO_DATE(p_ymd, 'YYYYMMDD'), 'DY', 'NLS_DATE_LANGUAGE=KOREAN');
END;
/
CREATE OR REPLACE FUNCTION FN_HOUR_BAND(p_hms VARCHAR2) RETURN VARCHAR2 IS
v_hh PLS_INTEGER := TO_NUMBER(SUBSTR(p_hms, 1, 2));
BEGIN
RETURN CASE WHEN v_hh < 11 THEN '1.오전'
WHEN v_hh < 14 THEN '2.점심'
WHEN v_hh < 17 THEN '3.오후'
WHEN v_hh < 20 THEN '4.저녁'
ELSE '5.야간' END;
END;
/
SELECT FN_DOW_NM(ORD_DT) AS dow, FN_HOUR_BAND(ORD_HMS) AS band, COUNT(*) AS ord_cnt
FROM ORD
GROUP BY FN_DOW_NM(ORD_DT), FN_HOUR_BAND(ORD_HMS)
ORDER BY 1, 2;
[DISPLAY_CURSOR (ALLSTATS LAST)]
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Used-Mem |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 35 |00:00:01.74 | 22422 | |
| 1 | SORT GROUP BY | | 1 | 1000K| 35 |00:00:01.74 | 22422 | 6144 (0)|
| 2 | TABLE ACCESS FULL | ORD | 1 | 1000K| 1000K|00:00:00.11 | 22422 | |
---------------------------------------------------------------------------------------------------
[V$SQL: 이 문장] ELAPSED_TIME 1.74 초 · CPU_TIME 1.71 초 · PLSQL_EXEC_TIME 1.12 초 · BUFFER_GETS 22,422
[세션 통계] recursive calls 3 · execute count 1
[비교: 일자별 리포트] SELECT ORD_DT, COUNT(*) FROM ORD GROUP BY ORD_DT · 0.51 초 · Buffers 22,422 · PLSQL_EXEC_TIME 0
DETERMINISTIC을 붙이거나 스칼라 서브쿼리로 감싸는 안이 여기서 효과가 있는지도 쓰시오.