Skip to content
튜닝 연습인덱스 스캔 효율과 테이블 액세스
ACC-031

점심 피크 분석에서 거르는 시간 컬럼을 인덱스에 넣었는데 블록은 3분의 1밖에 줄지 않았다

중급SELECT유통106 / 700

상황 ​

매장운영팀의 「점심 피크 분석」 대시보드는 분기를 고르면 매장마다 12시대(12:00:00 ~ 12:59:59) 판매 건수를 보여 준다. 30분마다 새로 고치고 점장 다섯 명도 수시로 열어서 하루 100번쯤 돈다. 조민우는 "테이블에서 거르는 SLE_HMS를 인덱스에 넣으면 테이블에 덜 간다"며 (SLE_DT, SLE_HMS) 인덱스를 INVISIBLE로 만들어 쟀다. 1,430블록이 921블록이 됐다. 조민우는 "점심 판매가 절반이니 절반쯤은 줄 줄 알았다"고 한다. 아래는 2023년 4분기다.

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록(블록당 약 109행) · 판매 날짜순으로 쌓였고, 하루 안에서는 판매번호(곧 시각) 순이다 · POS가 하루 약 700건을 INSERT한다
  • SLE_HMS VARCHAR2(6) 'HHMISS' · 2023년 4분기 62,700건 · 그중 12시대 33,451건(53%) · 매장별 6,631 ~ 6,759건
  • 인덱스 IX_CF10_SLE_DT (SLE_DT): BLEVEL 2 · 리프 1,393 · CLUSTERING_FACTOR 9,921(교재 03화 실측) · 「일별 판매 목록」 같은 다른 SQL도 쓴다
  • 조민우 안 IX_CF10_DT_HMS (SLE_DT, SLE_HMS): INVISIBLE · BLEVEL 2 · 리프 약 1,520 · 통계 최신

원본 SQL ​

sql
SELECT SHP_NO, COUNT(*) AS lunch_cnt
  FROM CF10
 WHERE SLE_DT  BETWEEN :v_from AND :v_to
   AND SLE_HMS BETWEEN '120000' AND '125959'
 GROUP BY SHP_NO
 ORDER BY SHP_NO

실행계획 학습용 예시 · 건수는 적재 데이터(CF10) 그대로 ​

xplan
[지금 · IX_CF10_SLE_DT]
-----------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                |      1 |        |      5 |00:00:00.03 |    1430 |
|   1 |  SORT GROUP BY                       |                |      1 |      5 |      5 |00:00:00.03 |    1430 |
|*  2 |   TABLE ACCESS BY INDEX ROWID BATCHED| CF10           |      1 |   2800 |  33451 |00:00:00.03 |    1430 |
|*  3 |    INDEX RANGE SCAN                  | IX_CF10_SLE_DT |      1 |    11K |  62700 |00:00:00.01 |     177 |
-----------------------------------------------------------------------------------------------------------------
   2 - filter(("SLE_HMS">='120000' AND "SLE_HMS"<='125959'))
   3 - access("SLE_DT">=:V_FROM AND "SLE_DT"<=:V_TO)

[조민우 안 · IX_CF10_DT_HMS · 세션에서만 켜고]
-----------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                |      1 |        |      5 |00:00:00.02 |     921 |
|   1 |  SORT GROUP BY                       |                |      1 |      5 |      5 |00:00:00.02 |     921 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| CF10           |      1 |   2800 |  33451 |00:00:00.02 |     921 |
|*  3 |    INDEX RANGE SCAN                  | IX_CF10_DT_HMS |      1 |   2800 |  33451 |00:00:00.01 |     192 |
-----------------------------------------------------------------------------------------------------------------
   3 - access("SLE_DT">=:V_FROM AND "SLE_HMS">='120000' AND "SLE_DT"<=:V_TO AND "SLE_HMS"<='125959')
       filter(("SLE_HMS">='120000' AND "SLE_HMS"<='125959'))

문제 ​

  1. 테이블로 넘기는 행은 62,700 → 33,451로 47% 줄었는데 테이블 쪽 Buffers는 1,253 → 729로 42%만 준 이유를, 남는 행이 어느 블록에 있는지로 설명하시오. 조민우 안에서 SLE_HMS가 access와 filter에 함께 나오는 이유도 설명하시오.
  2. 이 화면이 테이블을 읽지 않도록 인덱스를 설계하고 DDL을 작성하시오. 조민우 안과 기존 IX_CF10_SLE_DT를 어떻게 할지와 그 대가도 설명하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자