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

매장 인덱스를 두고도 매장별 누적 주문 순위가 주문 100만 건을 다 읽는다

중급DDL버거43 / 700

상황 ​

버거원 본사 대시보드의 「매장 순위」는 오픈 이후 누적 주문 수로 매장 순위를 매기고, 맨 아래에 '매장 미확인' 줄을 따로 보여 준다. 대시보드는 10분마다 새로 고친다. 조민우는 "매장 인덱스가 있는데 왜 테이블을 읽냐"며 WHERE SHOP_NO IS NOT NULL을 붙였다. 인덱스만 읽게 되어 빨라졌지만 '매장 미확인' 줄이 사라졌다.

테이블과 인덱스 ​

  • ORD: 1,000,000행 · 22,422블록(전체 스캔 22,239) · ORD_NO NUMBER(PK)
  • ORD_DT VARCHAR2(8) · ORD_HMS VARCHAR2(6) · SHOP_NO VARCHAR2(10): 세 컬럼 모두 NOT NULL 제약이 없다(적재 스크립트의 DDL 그대로)
  • ORD_DT와 ORD_HMS의 실제 NULL은 0건이다. SHOP_NO는 2013-03-02 ~ 03-05 키오스크 장애 때 매장 번호 없이 들어온 주문 1,204건이 NULL이다 (문제 설정: 교재의 ORD에는 SHOP_NO가 빈 행이 없다)
  • 인덱스 ORD_PK (ORD_NO): 리프 약 2,090 · ORD_X01 (ORD_DT, ORD_HMS): 리프 3,774 · IX_ORD_SHOP_DT (SHOP_NO, ORD_DT): 리프 3,310 · 통계 최신

원본 SQL ​

sql
-- [원본]
SELECT SHOP_NO, COUNT(*) AS ord_cnt
  FROM ORD
 GROUP BY SHOP_NO
 ORDER BY ord_cnt DESC;

-- [조민우의 수정안]
SELECT SHOP_NO, COUNT(*) AS ord_cnt
  FROM ORD
 WHERE SHOP_NO IS NOT NULL
 GROUP BY SHOP_NO
 ORDER BY ord_cnt DESC;

실행계획 학습용 예시 ​

xplan
[원본]
--------------------------------------------------------------------------------------------------
| Id  | Operation              | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |                |      1 |        |    201 |00:00:00.21 |   22239 |
|   1 |  SORT ORDER BY         |                |      1 |    200 |    201 |00:00:00.21 |   22239 |
|   2 |   HASH GROUP BY        |                |      1 |    200 |    201 |00:00:00.21 |   22239 |
|   3 |    TABLE ACCESS FULL   | ORD            |      1 |   1000K|   1000K|00:00:00.12 |   22239 |
--------------------------------------------------------------------------------------------------

[조민우의 수정안]
--------------------------------------------------------------------------------------------------
| Id  | Operation              | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |                |      1 |        |    200 |00:00:00.09 |    3318 |
|   1 |  SORT ORDER BY         |                |      1 |    200 |    200 |00:00:00.09 |    3318 |
|   2 |   HASH GROUP BY        |                |      1 |    200 |    200 |00:00:00.09 |    3318 |
|*  3 |    INDEX FAST FULL SCAN| IX_ORD_SHOP_DT |      1 |    999K|    999K|00:00:00.05 |    3318 |
--------------------------------------------------------------------------------------------------
   3 - filter("SHOP_NO" IS NOT NULL)

문제 ​

  1. IX_ORD_SHOP_DT가 있는데도 원본이 테이블 전체를 읽는 이유를 설명하시오. ORD_DT의 실제 NULL이 0건인데도 그렇게 되는 이유도 설명하시오.
  2. 조민우의 수정안이 틀린 이유를 설명하시오.
  3. '매장 미확인' 줄을 지키면서 인덱스만 읽도록 고치시오. 인덱스를 새로 만들지 않는 방법을 먼저 찾으시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자