테마
유통 POS 개편을 앞두고 박팀장이 판매(CF10)를 읽는 운영 SQL 넷을 넘기며 말했다. "인덱스는 하나만 더 늘릴 수 있다. 가능하면 늘리지 말고. SQL마다 무엇을 고칠지, 인덱스는 무엇으로 할지 정리해 와."
조회 빈도는 각 SQL 위의 주석에 적혀 있다.
CF10 판매: 500,000행 · 4,583블록(전체 스캔 4,510, 블록당 약 109행) · 모든 컬럼에 NOT NULL 제약SLE_NO VARCHAR2(15) '20230601-0001'(앞 8자리가 SLE_DT와 전부 같다, 공백이 든 값 0건) · SLE_DT VARCHAR2(8) · SHP_NO NUMBER(5) 1001 ~ 1005CF10_PK (SLE_NO): BLEVEL 2 · 리프 약 1,680 / IX_CF10_SLE_DT (SLE_DT): BLEVEL 2 · 리프 1,393 · CF 9,921 · 통계 최신-- S1 매장 「오늘 영수증 목록」: 매장 POS 5대가 수시로 (하루 약 2,000번)
SELECT SLE_NO, SLE_HMS
FROM CF10
WHERE TO_CHAR(SHP_NO) = :v_shp
AND SUBSTR(SLE_NO, 1, 8) = :v_dt
ORDER BY SLE_NO;
-- S2 본사 「주간 일별 건수」: 앱이 날짜를 숫자(20230605)로 넘긴다 (하루 약 300번)
SELECT SLE_DT, COUNT(*)
FROM CF10
WHERE TO_NUMBER(SLE_DT) BETWEEN :n_from AND :n_to
GROUP BY SLE_DT;
-- S3 고객센터 「영수증 조회」: 상담원이 붙여 넣은 번호에 공백이 섞여 온다 (하루 약 1,500번)
SELECT SLE_NO, SLE_DT, SLE_HMS, SHP_NO
FROM CF10
WHERE TRIM(SLE_NO) = :v_no;
-- S4 본사 「월 매장별 건수」 (하루 약 200번)
SELECT SHP_NO, COUNT(*)
FROM CF10
WHERE SLE_DT LIKE :v_ym || '%'
GROUP BY SHP_NO;
[S1 — 매장 1003 · 2023-06-01]
-----------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 146 |00:00:00.05 | 4510 |
| 1 | SORT ORDER BY | | 1 | 50 | 146 |00:00:00.05 | 4510 |
|* 2 | TABLE ACCESS FULL| CF10 | 1 | 50 | 146 |00:00:00.05 | 4510 |
-----------------------------------------------------------------------------------
2 - filter((TO_CHAR("SHP_NO")=:V_SHP AND SUBSTR("SLE_NO",1,8)=:V_DT))
[S2 — 2023-06-05 ~ 06-11]
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 7 |00:00:00.04 | 1406 |
| 1 | HASH GROUP BY | | 1 | 730 | 7 |00:00:00.04 | 1406 |
|* 2 | INDEX FAST FULL SCAN| IX_CF10_SLE_DT | 1 | 1250 | 4919 |00:00:00.03 | 1406 |
-----------------------------------------------------------------------------------------------
2 - filter((TO_NUMBER("SLE_DT")>=:N_FROM AND TO_NUMBER("SLE_DT")<=:N_TO))
[S3]
-----------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.04 | 4510 |
|* 1 | TABLE ACCESS FULL | CF10 | 1 | 5000 | 1 |00:00:00.04 | 4510 |
-----------------------------------------------------------------------------------
1 - filter(TRIM("SLE_NO")=:V_NO)
[S4 — 2023-06]
-------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 5 |00:00:00.02 | 478 |
| 1 | HASH GROUP BY | | 1 | 5 | 5 |00:00:00.02 | 478 |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED| CF10 | 1 | 693 | 20953 |00:00:00.02 | 478 |
|* 3 | INDEX RANGE SCAN | IX_CF10_SLE_DT | 1 | 693 | 20953 |00:00:00.01 | 62 |
-------------------------------------------------------------------------------------------------------------
3 - access("SLE_DT" LIKE :V_YM||'%')
filter("SLE_DT" LIKE :V_YM||'%')