테마
정산 모니터 화면을 새로 만든다. 화면 SQL 다섯 개(S1 ~ S5)가 모두 T_ORD_STLM을 읽는데, 이 테이블에는 인덱스가 하나도 없다. 매일 밤 비우고 직접 경로(/*+ APPEND */)로 10만 건을 다시 싣기 때문이다(DML-001).
박팀장의 주문은 이렇다. "인덱스는 최소로. 다섯 SQL이 각각 어느 인덱스를 어떻게 쓰는지 줄 단위로 보여 주고, 밤 적재에 무엇이 늘어나는지도 적어 줘" 화면 SQL은 개발자가 쓴 그대로다. 정산 담당 20명이 낮 동안 이 화면을 연다.
T_ORD_STLM 정산: 100,000행 · 640블록 · STLM_SEQ NOT NULL(정산 번호, 유일) · ORD_SEQ · CUS_ID · STLM_DT DATE NOT NULL(주문 시각을 옮긴 값, 시분초 있음, 2023-01-01 ~ 2024-12-31, 하루 약 137건) · STLM_AMTTRUNCATE 뒤 T_ORD 순서대로 적재한다. 하루치 정산과 한 고객의 정산 50건은 테이블 곳곳에 흩어져 있다-- S1 「최근 정산 20건」
SELECT STLM_SEQ, CUS_ID, STLM_DT, STLM_AMT
FROM T_ORD_STLM
ORDER BY STLM_DT DESC, STLM_SEQ DESC
FETCH FIRST 20 ROWS ONLY;
-- S2 「고객 마지막 정산일」
SELECT MAX(STLM_DT) FROM T_ORD_STLM WHERE CUS_ID = :cus;
-- S3 「고객 최근 정산 5건」
SELECT STLM_SEQ, STLM_DT, STLM_AMT
FROM T_ORD_STLM
WHERE CUS_ID = :cus
ORDER BY STLM_DT DESC, STLM_SEQ DESC
FETCH FIRST 5 ROWS ONLY;
-- S4 「그날 고액 정산 10건」
SELECT STLM_SEQ, CUS_ID, STLM_DT, STLM_AMT
FROM T_ORD_STLM
WHERE STLM_DT >= :d AND STLM_DT < :d + 1
AND ROWNUM <= 10
ORDER BY STLM_AMT DESC;
-- S5 「정산 데이터 기간」
SELECT MIN(STLM_DT), MAX(STLM_DT) FROM T_ORD_STLM;
[S1 · S2 · S3 · S5 는 TABLE ACCESS FULL 640블록 · S4 는 FULL 도중 10건에서 멈춤]
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------------
S1
| 0 | SELECT STATEMENT | | 1 | | 20 |00:00:00.03 | 640 |
|* 1 | VIEW | | 1 | 20 | 20 |00:00:00.03 | 640 |
|* 2 | WINDOW SORT PUSHED RANK| | 1 | 100K| 20 |00:00:00.03 | 640 |
| 3 | TABLE ACCESS FULL | T_ORD_STLM | 1 | 100K| 100K|00:00:00.01 | 640 |
S2
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 640 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 640 |
|* 2 | TABLE ACCESS FULL | T_ORD_STLM | 1 | 50 | 50 |00:00:00.01 | 640 |
S3
| 0 | SELECT STATEMENT | | 1 | | 5 |00:00:00.01 | 640 |
|* 1 | VIEW | | 1 | 5 | 5 |00:00:00.01 | 640 |
|* 2 | WINDOW SORT PUSHED RANK| | 1 | 50 | 5 |00:00:00.01 | 640 |
|* 3 | TABLE ACCESS FULL | T_ORD_STLM | 1 | 50 | 50 |00:00:00.01 | 640 |
S4
| 0 | SELECT STATEMENT | | 1 | | 10 |00:00:00.01 | 47 |
| 1 | SORT ORDER BY | | 1 | 10 | 10 |00:00:00.01 | 47 |
|* 2 | COUNT STOPKEY | | 1 | | 10 |00:00:00.01 | 47 |
|* 3 | FILTER | | 1 | | 10 |00:00:00.01 | 47 |
|* 4 | TABLE ACCESS FULL | T_ORD_STLM | 1 | 137 | 10 |00:00:00.01 | 47 |
S5
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 640 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 640 |
| 2 | TABLE ACCESS FULL | T_ORD_STLM | 1 | 100K| 100K|00:00:00.01 | 640 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
S1 2 - filter(ROW_NUMBER() OVER ( ORDER BY INTERNAL_FUNCTION("STLM_DT") DESC ,INTERNAL_FUNCTION("STLM_SEQ") DESC )<=20)
S2 2 - filter("CUS_ID"=:CUS)
S3 3 - filter("CUS_ID"=:CUS)
S4 2 - filter(ROWNUM<=10)
3 - filter(:D+1>:D)
4 - filter(("STLM_DT">=:D AND "STLM_DT"<:D+1))