Skip to content
튜닝 연습소트 튜닝과 페이징
SORT-012

같은 날은 주문 순서대로 보여 달라는 요청 하나에 장부 첫 화면이 14블록에서 5,826블록이 됐다

중급SELECT한빛유통317 / 700

상황 ​

정산팀의 「주문 장부」 첫 화면은 최신 주문 10건을 보여 준다. 09화 rev01 뒤로 인덱스를 거꾸로 읽어 14블록이면 끝났다. 지난주 정산팀이 "같은 날 주문은 주문번호 순서(먼저 들어온 것부터)로 보여 달라. 종이 장부와 대조하기 쉽게"라고 요청했고, 개발자는 ORD_SEQ DESC를 ORD_SEQ로 바꿔 배포했다. 배포 다음 날부터 이 화면이 0.3초씩 걸린다. 정산팀 30명이 하루 종일 연다.

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록 · ORD_DT DATE(전부 자정, 하루 913 ~ 914건이 같은 ORD_DT) · ORD_SEQ NOT NULL, 유일 · ORD_DT는 NULL 허용(실제 0건)
  • 인덱스 IX_ORD_BIG_DT_SEQ (ORD_DT, ORD_SEQ): BLEVEL 2 · 리프 3,344블록 · CF 1,000,000(교재 09화 rev01) · ORD_SEQ가 NOT NULL이라 모든 행이 인덱스에 있다
  • 이 인덱스는 기간 조회와 다른 화면의 키 기반 페이징(ORD_DT DESC, ORD_SEQ DESC)에도 쓰인다 · 통계 최신

원본 SQL ​

sql
SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_AMT
  FROM T_ORD_BIG
 ORDER BY ORD_DT DESC, ORD_SEQ
 FETCH FIRST 10 ROWS ONLY

실행계획 학습용 예시 · 교재 09화 rev01 규모 ​

xplan
[지난주까지 : ORDER BY ORD_DT DESC, ORD_SEQ DESC]
-------------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
-------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                   |      1 |        |     10 |00:00:00.01 |      14 |          |
|*  1 |  VIEW                         |                   |      1 |     10 |     10 |00:00:00.01 |      14 |          |
|*  2 |   WINDOW NOSORT STOPKEY       |                   |      1 |     10 |     10 |00:00:00.01 |      14 |          |
|   3 |    TABLE ACCESS BY INDEX ROWID| T_ORD_BIG         |      1 |   1000K|     10 |00:00:00.01 |      14 |          |
|   4 |     INDEX FULL SCAN DESCENDING| IX_ORD_BIG_DT_SEQ |      1 |     10 |     10 |00:00:00.01 |       4 |          |
-------------------------------------------------------------------------------------------------------------------

[지금 : ORDER BY ORD_DT DESC, ORD_SEQ]
-------------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
-------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                   |      1 |        |     10 |00:00:00.29 |    5826 |          |
|*  1 |  VIEW                         |                   |      1 |     10 |     10 |00:00:00.29 |    5826 |          |
|*  2 |   WINDOW SORT PUSHED RANK     |                   |      1 |   1000K|     10 |00:00:00.29 |    5826 | 4096  (0)|
|   3 |    TABLE ACCESS FULL          | T_ORD_BIG         |      1 |   1000K|   1000K|00:00:00.05 |    5826 |          |
-------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("from$_subquery$_002"."rowlimit_$$_rownumber"<=10)
   2 - filter(ROW_NUMBER() OVER ( ORDER BY INTERNAL_FUNCTION("ORD_DT") DESC ,"ORD_SEQ")<=10)

문제 ​

  1. 정렬 방향 하나를 바꿨을 뿐인데 실행계획이 인덱스 역순 읽기에서 테이블 전체 읽기와 정렬로 바뀐 까닭을 설명하시오.
  2. 정산팀의 요구(같은 날은 주문번호 오름차순)를 지키면서 다시 10건만 읽고 멈추도록 인덱스를 설계하고, 그 대가와 기존 인덱스를 어떻게 할지 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자