Skip to content
튜닝 연습인덱스 사용 조건
IDX-075

「주문 조회」 개편에 단일 컬럼 인덱스 셋을 달아 달라는 요청서와 한 달 치 조회 조건 통계가 함께 왔다

실전DDL한빛유통75 / 700

상황 ​

한빛유통 「주문 조회」 화면을 개편한다. 주문일자 범위는 필수이고(최대 31일), 고객번호, 주문상태, 결제수단은 선택이다. 결과는 최근 주문부터 50건씩 보여 준다. 개발팀은 고른 조건 조합마다 SQL을 따로 만들어 보내도록 짰고(동적 SQL), 인덱스 요청서에는 "조건으로 쓰는 컬럼마다 하나씩: CUS_ID, ORD_ST, PAY_TP"라고 적었다. 박팀장은 운영 화면의 한 달 치 조회 로그를 뽑아 건네며 이렇게 말했다. "조합마다 어떻게 찾아갈지부터 정하고, 인덱스는 최소로."

테이블과 인덱스 ​

  • T_ORD 주문: 100,000행 · 896블록 · ORD_DT DATE(시분초 있음, 2023-01-01 ~ 2024-12-31, 하루 약 137건, 같은 날짜의 주문은 테이블 곳곳에 흩어져 있다) · CUS_ID 2,000명(고객당 50건, 한 달에 약 2건) · ORD_ST COMPLETE 90% · PENDING 8% · CANCEL 2% · PAY_TP CARD · CASH · POINT가 1/3씩
  • 인덱스 PK_T_ORD (ORD_SEQ) · IX_T_ORD_DT (ORD_DT): BLEVEL 1 · 리프 265블록 · CF 약 99,986 · 통계 최신
  • 주문 INSERT 하루 약 140건 · 결제와 취소로 ORD_ST를 고치는 UPDATE 하루 약 300건
  • 한 달 치 조회 로그(약 9만 번)
조합 조건 비율 보통 고르는 기간
C1 기간 + 고객 48% 한 달
C2 기간 + 상태 22% 하루
C3 기간만 17% 하루 ~ 일주일
C4 기간 + 결제수단 9% 일주일
C5 기간 + 상태 + 결제수단 4% 하루

원본 SQL ​

sql
-- C1 (다른 조합도 모양이 같고 조건 한 줄만 다르다)
SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_ST, PAY_TP, ORD_AMT
  FROM T_ORD
 WHERE ORD_DT >= TO_DATE(:v_from, 'YYYYMMDD')
   AND ORD_DT <  TO_DATE(:v_to, 'YYYYMMDD') + 1
   AND CUS_ID = :v_cus
 ORDER BY ORD_DT DESC
 FETCH FIRST 50 ROWS ONLY;

-- C2 : … AND ORD_ST = :v_st          C3 : 조건 줄 없음
-- C4 : … AND PAY_TP = :v_tp          C5 : … AND ORD_ST = :v_st AND PAY_TP = :v_tp

-- 개발팀 요청서
CREATE INDEX IX_T_ORD_CUS ON T_ORD (CUS_ID);
CREATE INDEX IX_T_ORD_ST  ON T_ORD (ORD_ST);
CREATE INDEX IX_T_ORD_TP  ON T_ORD (PAY_TP);

실행계획 학습용 예시 ​

xplan
[C1 · 지금 · CUS_000777 · 2024년 6월]
-----------------------------------------------------------------------------------------------
| Id  | Operation                | Name  | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |       |      1 |        |      2 |00:00:00.01 |     896 |
|*  1 |  VIEW                    |       |      1 |      2 |      2 |00:00:00.01 |     896 |
|*  2 |   WINDOW SORT PUSHED RANK|       |      1 |      2 |      2 |00:00:00.01 |     896 |
|*  3 |    TABLE ACCESS FULL     | T_ORD |      1 |      2 |      2 |00:00:00.01 |     896 |
-----------------------------------------------------------------------------------------------
   1 - filter("from$_subquery$_002"."rowlimit_$$_rownumber"<=50)
   2 - filter(ROW_NUMBER() OVER ( ORDER BY INTERNAL_FUNCTION("ORD_DT") DESC )<=50)
   3 - filter(("CUS_ID"=:V_CUS AND "ORD_DT">=TO_DATE(:V_FROM,'YYYYMMDD') AND "ORD_DT"<TO_DATE(:V_TO,'YYYYMMDD')+1))

[C2 · 지금 · PENDING · 2024-06-14]
-------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |             |      1 |        |     11 |00:00:00.01 |     139 |
|*  1 |  VIEW                          |             |      1 |     11 |     11 |00:00:00.01 |     139 |
|*  2 |   WINDOW NOSORT STOPKEY        |             |      1 |     11 |     11 |00:00:00.01 |     139 |
|*  3 |    TABLE ACCESS BY INDEX ROWID | T_ORD       |      1 |     11 |     11 |00:00:00.01 |     139 |
|*  4 |     INDEX RANGE SCAN DESCENDING| IX_T_ORD_DT |      1 |    137 |    137 |00:00:00.01 |       2 |
-------------------------------------------------------------------------------------------------------
   1 - filter("from$_subquery$_002"."rowlimit_$$_rownumber"<=50)
   2 - filter(ROW_NUMBER() OVER ( ORDER BY INTERNAL_FUNCTION("ORD_DT") DESC )<=50)
   3 - filter("ORD_ST"=:V_ST)
   4 - access("ORD_DT"<TO_DATE(:V_TO,'YYYYMMDD')+1 AND "ORD_DT">=TO_DATE(:V_FROM,'YYYYMMDD'))

문제 ​

  1. 개발팀이 요청한 단일 컬럼 인덱스 셋이 각 조합에서 어떻게 쓰일지, 또는 쓰이지 않을지 설명하시오.
  2. 다섯 조합을 받치는 최소 인덱스 세트를 설계하고, 조합마다 어느 인덱스를 어떻게 쓰는지(access, filter, 정렬과 50건 멈춤) 설명하시오.
  3. 기존 IX_T_ORD_DT를 어떻게 할지와, 이 설계가 INSERT와 UPDATE에 주는 대가를 설명하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자