Skip to content
튜닝 연습서브쿼리와 쿼리 변환
SUBQ-041

하루 3,000번 여는 「미정산 주문」 화면이 20건을 보여 주려고 1,536블록을 읽는다

실전DDL한빛유통276 / 700

상황 ​

정산팀의 「미정산 주문」 화면은 결제가 끝났는데 아직 정산 행이 없는 주문을 최근 주문부터 20건씩 보여 준다. 정산 담당 10명이 새로 고치고 다음 쪽을 넘기며 하루 3,000번쯤 연다. 한 번에 0.05초라 아무도 불평하지 않지만, DBA의 상위 SQL 목록에서 이 화면이 하루 논리 읽기 약 461만 블록으로 3위에 올라 있다. 박팀장은 인덱스를 만들어도 좋다고 했다. 다만 밤마다 정산 테이블을 통째로 다시 넣는 적재 배치에 주는 영향과, 마지막 쪽에서 무슨 일이 생기는지까지 숫자로 적어 오라고 했다.

테이블과 인덱스 ​

  • T_ORD: 100,000행 · 896블록 · PK_T_ORD (ORD_SEQ) · COMPLETE 90,000행 · 오늘(2024-12-31) 주문 136건과 어제 주문 136건은 모두 COMPLETE · 하루치 주문은 테이블 전체에 흩어져 있다(ORD_SEQ가 주문 시각 순이 아니다)
  • T_ORD_STLM: 99,864행 · 640블록 · 인덱스 없음 · 01시 정산 적재 배치가 TRUNCATE 뒤 INSERT /*+ APPEND */로 전날까지의 결제분 전부를 다시 넣는다(0.1초 안팎)
  • 미정산은 늘 오늘 결제분뿐이다. 어제까지는 밤 배치가 넣고, 배치가 빠뜨린 건은 월초 보정 배치가 메운다 · 지금 미정산 136건(7쪽)
  • 통계 최신

원본 SQL ​

sql
SELECT *
  FROM (SELECT o.ORD_SEQ, o.CUS_ID, o.ORD_DT, o.PAY_DT, o.PAY_AMT
          FROM T_ORD o
         WHERE o.ORD_ST = 'COMPLETE'
           AND NOT EXISTS (SELECT 1 FROM T_ORD_STLM s WHERE s.ORD_SEQ = o.ORD_SEQ)
         ORDER BY o.ORD_DT DESC)
 WHERE ROWNUM <= 20

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

xplan
-------------------------------------------------------------------------------------------------------------
| Id  | Operation               | Name       | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |            |      1 |        |     20 |00:00:00.05 |    1536 |          |
|*  1 |  COUNT STOPKEY          |            |      1 |        |     20 |00:00:00.05 |    1536 |          |
|   2 |   VIEW                  |            |      1 |    136 |     20 |00:00:00.05 |    1536 |          |
|*  3 |    SORT ORDER BY STOPKEY|            |      1 |    136 |     20 |00:00:00.05 |    1536 | 2048  (0)|
|*  4 |     HASH JOIN RIGHT ANTI|            |      1 |    136 |    136 |00:00:00.05 |    1536 | 5932K (0)|
|   5 |      TABLE ACCESS FULL  | T_ORD_STLM |      1 |  99864 |  99864 |00:00:00.01 |     640 |          |
|*  6 |      TABLE ACCESS FULL  | T_ORD      |      1 |  90000 |  90000 |00:00:00.01 |     896 |          |
-------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(ROWNUM<=20)
   3 - filter(ROWNUM<=20)
   4 - access("S"."ORD_SEQ"="O"."ORD_SEQ")
   6 - filter("O"."ORD_ST"='COMPLETE')

문제 ​

  1. 20건을 보여 주는 화면이 1,536블록을 읽는 까닭을 실행계획 3 ~ 6번 줄로 설명하시오. ROWNUM이 있는데도 부분범위 처리가 안 되는 까닭도 쓰시오.
  2. 첫 쪽이 20건 남짓만 읽고 멈추도록 인덱스를 설계하고 SQL을 고치시오.
  3. 고친 뒤 마지막 쪽(16건)과 '다음 쪽 없음'을 확인할 때 일어나는 일, 인덱스가 야간 적재 배치에 주는 대가를 숫자로 서술하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자