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

고객 월 실적 적재가 지난달부터 논리 읽기 1위로 올라왔다

중급INSERT한빛유통286 / 700

상황 ​

월초 배치의 「고객 월 실적 적재」는 지난달 고객별 주문 건수와 금액을 T_CUS_MON에 넣는다. 지난달 배치 소스를 정리하면서 이 단계의 SELECT를 영업팀 「VIP 월 실적」 화면 SQL에서 그대로 옮겨 왔고, 그 뒤로 이 INSERT가 AWR 논리 읽기 1위에 올랐다. 화면 SQL의 힌트 옆에는 -- A 등급 20명만 보는 화면이라 고객마다 뷰를 연다라는 주석이 있다. 배치에는 등급 조건이 없다.

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록 · 고객 2,000명이 약 500건씩 · 한 달 약 27,390건(고객마다 약 14건) · 한 고객의 주문은 테이블 곳곳에 흩어져 있다
  • 인덱스 IX_ORD_BIG_CUS (CUS_ID): 콜센터 화면용 · 높이 3 · 고객 한 명의 항목이 리프 1~2블록
  • M_CUS: 2,000행 · 30블록 · PK_M_CUS (CUS_ID)
  • T_CUS_MON(이 문제에서 만든 집계 테이블): YM · CUS_ID · CUS_GD · ORD_CNT · ORD_AMT · 인덱스 없음 · 한 달에 2,000행씩 쌓는다
  • 바인드: :ym = '202406', :d1과 :d2는 6월 1일과 7월 1일 · 통계 최신

원본 SQL ​

sql
INSERT INTO T_CUS_MON (YM, CUS_ID, CUS_GD, ORD_CNT, ORD_AMT)
SELECT /*+ LEADING(c) USE_NL(v) NO_MERGE(v) PUSH_PRED(v) */
       :ym, c.CUS_ID, c.CUS_GD, v.ORD_CNT, v.ORD_AMT
  FROM M_CUS c,
       (SELECT CUS_ID, COUNT(*) AS ORD_CNT, SUM(ORD_AMT) AS ORD_AMT
          FROM T_ORD_BIG
         WHERE ORD_DT >= :d1
           AND ORD_DT <  :d2
         GROUP BY CUS_ID) v
 WHERE v.CUS_ID = c.CUS_ID

실행계획 학습용 예시 ​

xplan
-----------------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT                       |                |      1 |        |      0 |00:00:00.93 |    1008K|
|   1 |  LOAD TABLE CONVENTIONAL               | T_CUS_MON      |      1 |        |      0 |00:00:00.93 |    1008K|
|   2 |   NESTED LOOPS                         |                |      1 |   2000 |   2000 |00:00:00.91 |    1008K|
|   3 |    TABLE ACCESS FULL                   | M_CUS          |      1 |   2000 |   2000 |00:00:00.01 |      30 |
|   4 |    VIEW PUSHED PREDICATE               |                |   2000 |      1 |   2000 |00:00:00.90 |    1008K|
|   5 |     SORT GROUP BY                      |                |   2000 |      1 |   2000 |00:00:00.89 |    1008K|
|*  6 |      TABLE ACCESS BY INDEX ROWID BATCHED| T_ORD_BIG     |   2000 |     14 |  27390 |00:00:00.85 |    1008K|
|*  7 |       INDEX RANGE SCAN                 | IX_ORD_BIG_CUS |   2000 |    500 |   1000K|00:00:00.12 |    8000 |
-----------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   6 - filter(("ORD_DT">=:D1 AND "ORD_DT"<:D2))
   7 - access("CUS_ID"="C"."CUS_ID")

문제 ​

  1. 1,008K 블록이 어디서 나오는지 4번, 6번, 7번 줄의 Starts와 A-Rows로 설명하시오. 화면에서는 문제가 없던 힌트가 배치에서 문제가 된 이유도 설명하시오.
  2. 배치 INSERT를 고치고 고친 뒤의 처리 방식과 읽는 블록 수를 쓰시오. 원래 화면 SQL도 더 낫게 할 방법이 있으면 함께 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자