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

연간으로 고르면 일별 매출 그래프의 선이 앞뒤로 엉킨다

중급SELECT한빛유통112 / 700

상황 ​

재무팀의 「일별 매출 추이」는 기간을 고르면 일자별 주문 건수와 매출을 선 그래프로 그린다. 그래프 부품은 받은 행 순서대로 점을 잇는다. 한 달을 고를 때는 문제가 없었는데, 올해부터 1년을 고르기 시작하자 선이 앞뒤로 엉킨 그림이 나온다. SQL에는 개발 당시의 주석 -- 인덱스가 날짜순이라 ORDER BY 는 뺐다 (정렬 비용 절약)이 남아 있다. 월간 메뉴와 연간 메뉴는 화면이 달라 커서도 따로 있다.

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록 · ORD_DT 2022-01-01 ~ 2024-12-30(하루 913건, 전부 자정) · 2024년 6월 27,390건 · 2024년 333,245건(365일)
  • 인덱스 IX_ORD_BIG_DT_AMT (ORD_DT, ORD_AMT): BLEVEL 2 · 리프 3,206블록(교재 04화 실측) · 통계 최신 · 다른 인덱스 없음
  • db_file_multiblock_read_count = 128

원본 SQL ​

sql
SELECT ORD_DT, COUNT(*) AS ord_cnt, SUM(ORD_AMT) AS ord_amt
  FROM T_ORD_BIG
 WHERE ORD_DT >= TO_DATE(:v_from, 'YYYYMMDD')
   AND ORD_DT <  TO_DATE(:v_to, 'YYYYMMDD') + 1
 GROUP BY ORD_DT
 -- 인덱스가 날짜순이라 ORDER BY 는 뺐다 (정렬 비용 절약)

실행계획 학습용 예시 · 범위별 Buffers(한 달 91, 1년 FAST FULL SCAN 3,227)는 교재 04화 실측 ​

xplan
[월간 메뉴 — 2024년 6월]
-----------------------------------------------------------------------------------------------------
| Id  | Operation             | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |                   |      1 |        |     30 |00:00:00.01 |      91 |
|   1 |  SORT GROUP BY NOSORT |                   |      1 |     30 |     30 |00:00:00.01 |      91 |
|*  2 |   FILTER              |                   |      1 |        |  27390 |00:00:00.01 |      91 |
|*  3 |    INDEX RANGE SCAN   | IX_ORD_BIG_DT_AMT |      1 |  27390 |  27390 |00:00:00.01 |      91 |
-----------------------------------------------------------------------------------------------------
   2 - filter(TO_DATE(:V_TO,'YYYYMMDD')+1>TO_DATE(:V_FROM,'YYYYMMDD'))
   3 - access("ORD_DT">=TO_DATE(:V_FROM,'YYYYMMDD') AND "ORD_DT"<TO_DATE(:V_TO,'YYYYMMDD')+1)

[연간 메뉴 — 2024년]
----------------------------------------------------------------------------------------------------------------
| Id  | Operation              | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |                   |      1 |        |    365 |00:00:00.12 |    3227 |          |
|   1 |  HASH GROUP BY         |                   |      1 |    365 |    365 |00:00:00.12 |    3227 | 1489K (0)|
|*  2 |   FILTER               |                   |      1 |        |    333K|00:00:00.09 |    3227 |          |
|*  3 |    INDEX FAST FULL SCAN| IX_ORD_BIG_DT_AMT |      1 |    334K|    333K|00:00:00.07 |    3227 |          |
----------------------------------------------------------------------------------------------------------------
   2 - filter(TO_DATE(:V_TO,'YYYYMMDD')+1>TO_DATE(:V_FROM,'YYYYMMDD'))
   3 - filter(("ORD_DT">=TO_DATE(:V_FROM,'YYYYMMDD') AND "ORD_DT"<TO_DATE(:V_TO,'YYYYMMDD')+1))

[연간 메뉴 · INDEX_RS 힌트로 범위 스캔을 강제하면] 약 1,072블록 (리프 3,206 × 33.3% + 수직 탐색)

문제 ​

  1. 월간 메뉴와 연간 메뉴의 1번, 3번 줄을 비교해 연간 조회에서만 행 순서가 섞이는 이유를 설명하시오.
  2. 연간 조회에서 옵티마이저가 범위 스캔(약 1,072블록)보다 Buffers가 많은 INDEX FAST FULL SCAN(3,227블록)을 고른 이유를 블록을 읽는 방식으로 설명하시오.
  3. 기간과 상관없이 일자순으로 나오도록 SQL을 고치고, 고친 뒤 정렬에 드는 비용을 설명하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자