테마
고객센터 상담 화면은 고객 번호를 넣으면 세 칸을 띄운다. 이 세 SQL을 위해 T_ORD_BIG에는 고객 번호로 시작하는 인덱스가 둘 있다. 오과장은 "주문이 들어올 때마다 고객 인덱스를 둘씩 고친다. 하나로 합칠 수 없나?"라고 물었다.
조민우는 (CUS_ID, ORD_ST, ORD_DT) 하나로 합치자고 했다. INVISIBLE로 만들어 세션에서 재 보니 S2와 S3은 거의 그대로인데 S1이 24블록에서 505블록으로 늘었다.
T_ORD_BIG: 1,000,000행 · 5,826블록 · CUS_ID 2,000명(고객당 500건, 한 고객의 주문은 테이블 곳곳의 서로 다른 블록에 있다) · ORD_ST COMPLETE 90% · PENDING 8% · CANCEL 2%(고객마다 섞여 있다) · ORD_DT 전부 자정IX_ORD_BIG_CUS_DT (CUS_ID, ORD_DT): BLEVEL 2 · 리프 3,640블록 · S1, S3이 쓴다IX_ORD_BIG_CUS_ST (CUS_ID, ORD_ST): ACC-039에서 S2용으로 만든 인덱스 · BLEVEL 2 · 리프 약 3,580블록IX_ORD_BIG_CUS_ST_DT (CUS_ID, ORD_ST, ORD_DT): INVISIBLE · BLEVEL 2 · 리프 약 4,350블록IX_ORD_BIG_DT_AMT (ORD_DT, ORD_AMT) · 주문은 운영에서 계속 INSERT된다(인덱스 하나에 5만 건당 redo 약 12MB, 교재 14화)-- S1 「최근 주문 20건」 상담 화면 첫 칸 (하루 약 1만 번)
SELECT *
FROM (SELECT ORD_SEQ, ORD_DT, ORD_ST, ORD_AMT
FROM T_ORD_BIG
WHERE CUS_ID = :v_cus
ORDER BY ORD_DT DESC)
WHERE ROWNUM <= 20;
-- S2 「고객 주문 상태 요약」 (하루 약 3천 번)
SELECT ORD_ST, COUNT(*) AS ord_cnt
FROM T_ORD_BIG
WHERE CUS_ID = :v_cus
GROUP BY ORD_ST;
-- S3 「기간 주문 이력」 (하루 약 2천 번)
SELECT ORD_SEQ, ORD_DT, ORD_ST, ORD_AMT
FROM T_ORD_BIG
WHERE CUS_ID = :v_cus
AND ORD_DT >= TO_DATE(:v_from, 'YYYYMMDD')
ORDER BY ORD_DT DESC;
-- 조민우 안 : 재 보고 문제가 없으면 IX_ORD_BIG_CUS_DT, IX_ORD_BIG_CUS_ST 를 지운다
CREATE INDEX IX_ORD_BIG_CUS_ST_DT ON T_ORD_BIG (CUS_ID, ORD_ST, ORD_DT) INVISIBLE;
[S1 · 지금 · CUS_000123]
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 20 |00:00:00.01 | 24 |
|* 1 | COUNT STOPKEY | | 1 | | 20 |00:00:00.01 | 24 |
| 2 | VIEW | | 1 | 20 | 20 |00:00:00.01 | 24 |
| 3 | TABLE ACCESS BY INDEX ROWID | T_ORD_BIG | 1 | 500 | 20 |00:00:00.01 | 24 |
|* 4 | INDEX RANGE SCAN DESCENDING| IX_ORD_BIG_CUS_DT | 1 | 500 | 20 |00:00:00.01 | 4 |
------------------------------------------------------------------------------------------------------------
1 - filter(ROWNUM<=20)
4 - access("CUS_ID"=:V_CUS)
[S1 · 조민우 안 (세션에서 잰 것) · CUS_000123]
-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 20 |00:00:00.01 | 505 |
|* 1 | COUNT STOPKEY | | 1 | | 20 |00:00:00.01 | 505 |
| 2 | VIEW | | 1 | 500 | 20 |00:00:00.01 | 505 |
|* 3 | SORT ORDER BY STOPKEY | | 1 | 500 | 20 |00:00:00.01 | 505 |
| 4 | TABLE ACCESS BY INDEX ROWID BATCHED| T_ORD_BIG | 1 | 500 | 500 |00:00:00.01 | 505 |
|* 5 | INDEX RANGE SCAN | IX_ORD_BIG_CUS_ST_DT | 1 | 500 | 500 |00:00:00.01 | 5 |
-------------------------------------------------------------------------------------------------------------------
1 - filter(ROWNUM<=20)
3 - filter(ROWNUM<=20)
5 - access("CUS_ID"=:V_CUS)
[S2 · 조민우 안] INDEX RANGE SCAN · access("CUS_ID"=:V_CUS) · 인덱스만 · Buffers 5 (지금 IX_ORD_BIG_CUS_ST로 4)
[S3 · 조민우 안] INDEX RANGE SCAN · access("CUS_ID"=:V_CUS) filter("ORD_DT">=…) · 테이블 42행과 정렬 · Buffers 47 (지금 46)