테마
지난 주말 한빛유통은 T_ORD_BIG의 2022년 주문을 아카이브 테이블로 떼어 냈다. 화면 SQL을 고치지 않으려고 원래 테이블은 CTAS로 두 개(T_ORD_BIG_CUR, T_ORD_BIG_ARC)로 나눠 만든 뒤 지우고, 원래 이름으로 UNION ALL 뷰를 만들었다. 아카이브는 "조회가 거의 없을 테니" 인덱스를 만들지 않았다.
월요일에 잰 결과는 다음과 같다. (A) 정산 화면 「기간 매출」(하루 3,000번) 91 → 2,033블록, (B) 콜센터 「고객 최근 주문 10건」(하루 30,000번) 13 → 2,278블록, (C) 월 리포트 「3년 등급별 매출」(하루 20번) 5,856 → 5,856블록. 박팀장은 뷰를 유지할지, 파티션으로 갈지까지 포함해 안을 내라고 했다.
T_ORD_BIG_CUR(2023 ~ 2024년): 666,000행 · 3,884블록 · IX_CUR_DT_AMT (ORD_DT, ORD_AMT) · IX_CUR_CUS_DT (CUS_ID, ORD_DT) (원래 테이블의 인덱스를 다시 만들었다)T_ORD_BIG_ARC(2022년): 334,000행 · 1,942블록 · 인덱스 없음 · 앞으로 DML이 없다M_CUS: 2,000행 · 30블록 · 두 테이블 모두 통계 최신 · Partitioning 옵션은 라이선스가 있다-- 원래 이름의 뷰
CREATE OR REPLACE VIEW T_ORD_BIG AS
SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_ST, ORD_AMT FROM T_ORD_BIG_CUR
UNION ALL
SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_ST, ORD_AMT FROM T_ORD_BIG_ARC;
-- (A) 기간 매출 (하루 3,000번)
SELECT COUNT(*), SUM(ORD_AMT)
FROM T_ORD_BIG
WHERE ORD_DT >= :d1
AND ORD_DT < :d2;
-- (B) 고객 최근 주문 10건 (하루 30,000번)
SELECT *
FROM (SELECT ORD_SEQ, ORD_DT, ORD_ST, ORD_AMT
FROM T_ORD_BIG
WHERE CUS_ID = :cus
ORDER BY ORD_DT DESC)
WHERE ROWNUM <= 10;
-- (C) 3년 등급별 매출 (하루 20번) : M_CUS와 해시 조인, 두 테이블 전체 스캔 · 5,856블록 · 고칠 것 없음
[A · 2024년 6월]
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.03 | 2033 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.03 | 2033 |
| 2 | VIEW | T_ORD_BIG | 1 | 27391 | 27390 |00:00:00.03 | 2033 |
| 3 | UNION-ALL | | 1 | | 27390 |00:00:00.03 | 2033 |
|* 4 | INDEX RANGE SCAN | IX_CUR_DT_AMT | 1 | 27390 | 27390 |00:00:00.01 | 91 |
|* 5 | TABLE ACCESS FULL | T_ORD_BIG_ARC | 1 | 1 | 0 |00:00:00.02 | 1942 |
----------------------------------------------------------------------------------------------
4 - access("ORD_DT">=:D1 AND "ORD_DT"<:D2)
5 - filter(("ORD_DT">=:D1 AND "ORD_DT"<:D2))
[B]
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 10 |00:00:00.04 | 2278 |
|* 1 | COUNT STOPKEY | | 1 | | 10 |00:00:00.04 | 2278 |
| 2 | VIEW | | 1 | 500 | 10 |00:00:00.04 | 2278 |
|* 3 | SORT ORDER BY STOPKEY | | 1 | 500 | 10 |00:00:00.04 | 2278 |
| 4 | VIEW | T_ORD_BIG | 1 | 500 | 500 |00:00:00.04 | 2278 |
| 5 | UNION-ALL | | 1 | | 500 |00:00:00.04 | 2278 |
| 6 | TABLE ACCESS BY INDEX ROWID BATCHED| T_ORD_BIG_CUR | 1 | 333 | 333 |00:00:00.01 | 336 |
|* 7 | INDEX RANGE SCAN | IX_CUR_CUS_DT | 1 | 333 | 333 |00:00:00.01 | 3 |
|* 8 | TABLE ACCESS FULL | T_ORD_BIG_ARC | 1 | 167 | 167 |00:00:00.03 | 1942 |
-----------------------------------------------------------------------------------------------------------
1 - filter(ROWNUM<=10)
3 - filter(ROWNUM<=10)
7 - access("CUS_ID"=:CUS)
8 - filter("CUS_ID"=:CUS)
ROWNUM <= 10이 어디에 걸렸는지도 쓰시오.T_ORD_BIG을 연도별 RANGE 파티션 테이블로 바꾸는 안과 비교해 장단점을 쓰시오.