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

지난주부터 월말 정산 점검 계획에 카티션 조인이 끼어 있다

중급SELECT한빛유통599 / 700

상황 ​

정산팀의 월말 점검 배치(PL/SQL)는 점검 대상 주문 번호를 전역 임시 테이블 GT_STLM_TGT에 약 40,000건 넣고, 상품 유형 10종마다 아래 SQL을 한 번씩 돌린다. 지난달까지 1초 안에 끝나던 점검이 이번 달에는 20초를 넘겼다. 지난주 월요일 DBA가 주간 통계 잡을 바꿨다. "임시 테이블도 빠짐없이" 모으려고 GATHER_SCHEMA_STATS(..., gather_temp => TRUE)로 고쳤다. 이 DB는 2016년 11g에서 올릴 때 업체가 GLOBAL_TEMP_TABLE_STATS를 SHARED로 걸어 두었다.

테이블과 인덱스 ​

  • GT_STLM_TGT: 전역 임시 테이블(ON COMMIT PRESERVE ROWS) · ORD_SEQ · 배치 세션에서 40,000행(약 72블록)
  • GT_STLM_TGT 통계: NUM_ROWS 0 · BLOCKS 0 · SCOPE SHARED · 마지막 수집 지난주 월요일 03:12(DBA 세션에서 모았다. 그 세션의 임시 테이블은 비어 있었다)
  • T_ORD_DET: 200,000행 · 896블록 · PK_T_ORD_DET (ORD_SEQ, ORD_DET_NO) · 주문당 1 ~ 3건(평균 2건) · 통계 최신
  • M_ITM: 100행 · 5블록 · ITM_TP 10종(유형마다 상품 10개) · PK_M_ITM
  • 대상 주문 40,000건의 상세는 약 80,000건이고, 한 유형은 그중 약 8,000건이다.

원본 SQL ​

sql
SELECT i.ITM_ID, i.ITM_NM, COUNT(*) AS det_cnt, SUM(d.ORD_QTY * d.UNT_PRC) AS amt
  FROM GT_STLM_TGT g, T_ORD_DET d, M_ITM i
 WHERE d.ORD_SEQ = g.ORD_SEQ
   AND i.ITM_ID  = d.ITM_ID
   AND i.ITM_TP  = :tp
 GROUP BY i.ITM_ID, i.ITM_NM

실행계획 학습용 예시 ​

xplan
[유형 하나 · 이번 달 점검]
----------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name         | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |              |      1 |        |     10 |00:00:01.93 |    1270K|
|   1 |  HASH GROUP BY                 |              |      1 |      1 |     10 |00:00:01.93 |    1270K|
|   2 |   NESTED LOOPS                 |              |      1 |      1 |   8012 |00:00:01.92 |    1270K|
|   3 |    NESTED LOOPS                |              |      1 |      1 |    800K|00:00:00.98 |     470K|
|   4 |     MERGE JOIN CARTESIAN       |              |      1 |      1 |    400K|00:00:00.12 |      77 |
|*  5 |      TABLE ACCESS FULL         | M_ITM        |      1 |     10 |     10 |00:00:00.01 |       5 |
|   6 |      BUFFER SORT               |              |     10 |      1 |    400K|00:00:00.08 |      72 |
|   7 |       TABLE ACCESS FULL        | GT_STLM_TGT  |      1 |      1 |  40000 |00:00:00.01 |      72 |
|*  8 |     INDEX RANGE SCAN           | PK_T_ORD_DET |    400K|      1 |    800K|00:00:00.71 |     470K|
|*  9 |    TABLE ACCESS BY INDEX ROWID | T_ORD_DET    |    800K|      1 |   8012 |00:00:00.82 |     800K|
----------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   5 - filter("I"."ITM_TP"=:TP)
   8 - access("D"."ORD_SEQ"="G"."ORD_SEQ")
   9 - filter("I"."ITM_ID"="D"."ITM_ID")

[USER_TAB_STATISTICS — GT_STLM_TGT]
 NUM_ROWS  BLOCKS  LAST_ANALYZED        SCOPE
        0       0  지난주 월 03:12      SHARED

문제 ​

  1. 4번 줄의 MERGE JOIN CARTESIAN이 왜 나왔는지 E-Rows와 통계의 출처로 설명하시오. 지난달까지는 같은 SQL이 왜 괜찮았는지도 쓰시오.
  2. 이 임시 테이블의 통계를 바로잡고, 배치가 앞으로도 맞는 숫자를 쓰게 하는 SQL을 쓰시오. 주간 잡은 어떻게 할지와 그 대가도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자