Skip to content
튜닝 연습옵티마이저와 통계
OPT-013

매달 1일 아침에만 월마감 등급별 매출 보고서가 느리다

중급DDL한빛유통603 / 700

상황 ​

한빛유통은 6월부터 월마감 주문을 월별 파티션 테이블 T_ORD_CLS에 싣는다. 매달 25일 「칸 추가」 작업이 다음 달 칸을 미리 만들고, 1일 01:00 월마감 배치가 지난달 주문을 그 달 칸에 일반 INSERT로 싣는다. 영업기획팀은 1일 09:00부터 「월마감 등급별 매출」 보고서를 수십 번 연다. 박팀장: "1일에만 느리고 2일부터는 괜찮답니다. 7월 1일 오늘도 그래요."

테이블과 인덱스 ​

  • T_ORD_CLS: T_ORD_BIG와 같은 열 · 주문일(ORD_DT) 월별 Range 파티션(14화 T_ORD_BIG_P14와 같은 설계, PMAX 없음) · 2022년 1월부터 칸이 있다(6월 칸이 30번째) · 칸마다 약 27,000 ~ 28,000행, 약 160블록 · 인덱스 없음
  • 6월 칸 P202406: 5월 25일 생성 · 7월 1일 01:00에 27,390행 적재
  • P202406 통계: NUM_ROWS 0 · BLOCKS 0 · 마지막 수집 5월 25일 22:14(자동 수집)
  • 5월 칸 P202405: 28,302행 · 165블록 · 통계 6월 1일 22:09
  • M_CUS: 2,000행 · 30블록 · PK_M_CUS
  • 자동 통계 수집: 평일 22:00부터 4시간 · 통계가 없거나 STALE인 칸을 모은다

원본 SQL ​

sql
-- 매달 25일 · 칸 추가 (다음 달 칸을 미리 만든다)
ALTER TABLE T_ORD_CLS ADD PARTITION P202406 VALUES LESS THAN (DATE '2024-07-01');

-- 매달 1일 01:00 · 월마감 적재 (일반 INSERT)
INSERT INTO T_ORD_CLS (ORD_SEQ, CUS_ID, ORD_DT, ORD_ST, ORD_AMT)
SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_ST, ORD_AMT
  FROM T_ORD_BIG
 WHERE ORD_DT >= DATE '2024-06-01' AND ORD_DT < DATE '2024-07-01';
COMMIT;

-- 1일 09:00부터 · 「월마감 등급별 매출」
SELECT c.CUS_GD, COUNT(*) AS ord_cnt, SUM(o.ORD_AMT) AS amt
  FROM T_ORD_CLS o, M_CUS c
 WHERE o.ORD_DT >= DATE '2024-06-01' AND o.ORD_DT < DATE '2024-07-01'
   AND c.CUS_ID = o.CUS_ID
 GROUP BY c.CUS_GD

실행계획 학습용 예시 ​

xplan
[7월 1일 09:10 · 보고서]
-----------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |           |      1 |        |      3 |00:00:00.09 |   28155 |       |       |
|   1 |  HASH GROUP BY                |           |      1 |      1 |      3 |00:00:00.09 |   28155 |       |       |
|   2 |   NESTED LOOPS                |           |      1 |      1 |  27390 |00:00:00.08 |   28155 |       |       |
|   3 |    NESTED LOOPS               |           |      1 |      1 |  27390 |00:00:00.04 |     765 |       |       |
|   4 |     PARTITION RANGE SINGLE    |           |      1 |      1 |  27390 |00:00:00.01 |     163 |    30 |    30 |
|   5 |      TABLE ACCESS FULL        | T_ORD_CLS |      1 |      1 |  27390 |00:00:00.01 |     163 |    30 |    30 |
|*  6 |     INDEX UNIQUE SCAN         | PK_M_CUS  |  27390 |      1 |  27390 |00:00:00.02 |     602 |       |       |
|   7 |    TABLE ACCESS BY INDEX ROWID| M_CUS     |  27390 |      1 |  27390 |00:00:00.03 |   27390 |       |       |
-----------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   6 - access("C"."CUS_ID"="O"."CUS_ID")

[USER_TAB_PARTITIONS — T_ORD_CLS · 7월 1일 09:10]
 PARTITION_NAME   NUM_ROWS   BLOCKS   LAST_ANALYZED    실제 COUNT(*)
 P202405            28,302      165   06-01 22:09            28,302
 P202406                 0        0   05-25 22:14            27,390
 P202407                 0        0   06-25 22:11                 0

[7월 2일 09:10 · 같은 보고서]  HASH JOIN · Buffers 193 (7월 1일 22:07 자동 수집이 P202406 을 다시 모은 뒤)

문제 ​

  1. 2번 줄이 NL이 된 이유를 4번과 5번 줄의 E-Rows와 칸 통계로 설명하시오. 2일부터는 괜찮아지는 까닭도 쓰시오.
  2. 칸을 만들 때와 적재한 뒤에 할 일을 SQL로 쓰고, 두 방법의 대가를 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자