테마
매장 관리자 화면 「월간 영수증 목록」은 영수증마다 합계, 부가세, 사은품 대상 여부를 보여 준다. 명동점 12월 목록이 0.4초쯤 걸려 다른 화면보다 눈에 띄게 늦다. SQL을 짠 개발 담당자는 "합계 함수는 인라인 뷰에서 한 번만 부르게 짰고, 바깥에서는 그 컬럼을 쓰기만 한다"라고 한다. 그런데 DBA가 본 함수 안 SQL의 실행 횟수는 화면 행 수의 정확히 세 배였다.
CF10 판매(영수증): 500,000행 · 4,583블록 · SLE_DT VARCHAR2(8) · 2023년 12월 20,864건 · 그중 명동점(SHP_NO 1003) 4,176건IX_CF10_SLE_DT (SLE_DT): 이 문제에서는 있다CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) 높이 3 · 영수증 한 장에 평균 2.2행CREATE OR REPLACE FUNCTION FN_SLE_AMT(p_sle_no VARCHAR2) RETURN NUMBER IS
v_amt NUMBER;
BEGIN
SELECT SUM(SLE_QTY * SLE_UPRC) INTO v_amt
FROM CF11
WHERE SLE_NO = p_sle_no;
RETURN v_amt;
END;
/
SELECT v.SLE_NO, v.SLE_DT, v.SLE_HMS,
v.amt,
ROUND(v.amt / 11) AS vat,
CASE WHEN v.amt >= 30000 THEN 'Y' ELSE 'N' END AS gift_yn
FROM (SELECT h.SLE_NO, h.SLE_DT, h.SLE_HMS,
FN_SLE_AMT(h.SLE_NO) AS amt -- 합계는 여기서 한 번만
FROM CF10 h
WHERE h.SHP_NO = :shp_no
AND h.SLE_DT BETWEEN :fr AND :to) v
ORDER BY v.SLE_DT, v.SLE_HMS;
[DISPLAY_CURSOR (FORMAT => 'ALLSTATS LAST +PROJECTION') · :shp_no = 1003 · 2023년 12월]
---------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Used-Mem |
---------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 4176 |00:00:00.39 | 290 | |
| 1 | SORT ORDER BY | | 1 | 4167 | 4176 |00:00:00.39 | 290 | 407K (0)|
|* 2 | TABLE ACCESS BY INDEX ROWID BATCHED| CF10 | 1 | 4167 | 4176 |00:00:00.01 | 290 | |
|* 3 | INDEX RANGE SCAN | IX_CF10_SLE_DT | 1 | 20833 | 20864 |00:00:00.01 | 98 | |
---------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("H"."SHP_NO"=:SHP_NO)
3 - access("H"."SLE_DT">=:FR AND "H"."SLE_DT"<=:TO)
Column Projection Information (1번 줄만):
---------------------------------------------------
1 - (#keys=2) "H"."SLE_DT"[VARCHAR2,8], "H"."SLE_HMS"[VARCHAR2,6], "H"."SLE_NO"[VARCHAR2,15],
"PUP"."FN_SLE_AMT"("H"."SLE_NO")[22], ROUND("PUP"."FN_SLE_AMT"("H"."SLE_NO")/11)[22],
CASE WHEN "PUP"."FN_SLE_AMT"("H"."SLE_NO")>=30000 THEN 'Y' ELSE 'N' END [1]
[V$SQL: 함수 안의 문장 (dep=1) · 이 실행 한 번의 증가분]
SELECT SUM(SLE_QTY * SLE_UPRC) FROM CF11 WHERE SLE_NO = :B1 EXECUTIONS 12,528 · BUFFER_GETS 50,112 (한 번에 4블록)
[세션 통계] recursive calls 12,537 · session logical reads 50,415