Skip to content
튜닝 연습파싱과 바인드 변수
PARSE-025

분석용 사본을 읽는 본사 화면 넷 중 매장 화면만 날마다 다른 쪽이 느려진다

실전SELECT버거570 / 700

상황 ​

본사 분석용 사본 ORD_RANDOM을 읽는 화면이 넷 있다. 모두 바인드 변수로 쓰여 있다. 그중 점장이 쓰는 「매장 기간 주문 목록」(S1)은 기간이 짧은 날 느리다가, 다른 날은 긴 기간이 끝없이 돈다. 「매장 최근 주문」(S3)은 늘 0.3초쯤 걸린다. 박팀장이 말했다. "S1에 힌트를 박을지, 문장을 나눌지, 인덱스를 만들지 네 화면을 같이 보고 정해 주세요. 사본은 매일 밤 다시 적재하는데, 적재가 06:00 전에 끝나야 합니다."

테이블과 인덱스 ​

  • ORD_RANDOM: 1,000,000행 · 22,414블록 · POS 원장 ORD를 무작위 순서로 복제한 사본 · ORD_DT VARCHAR2(8) 하루 약 1,370건 · SHOP_NO 'SH0001' ~ 'SH0200' 매장마다 고르게(하루 약 7건) · ORD_NO 유일
  • 인덱스 ORD_RANDOM_X01 (ORD_DT, ORD_HMS): CF 999,958 · BLEVEL 2 · 리프 3,774블록 · 다른 인덱스는 없다
  • 매일 01:00부터 사본 여러 개를 다시 적재하며 06:00 전에 끝나야 한다. ORD_RANDOM 몫은 TRUNCATE, 직접 경로 적재, X01 유지, 통계 수집을 합쳐 지금 약 2분이다.
  • 바인드 피킹 켬 · 히스토그램 없음 · 화면 호출 수(하루): S1 3,000 · S2 2,000 · S3 4,000 · S4 30

원본 SQL ​

sql
-- S1 매장 기간 주문 목록 (점장 · 하루 ~ 3개월)
SELECT ORD_NO, ORD_DT, ORD_HMS
  FROM ORD_RANDOM
 WHERE SHOP_NO = :shop
   AND ORD_DT BETWEEN :f AND :t
 ORDER BY ORD_DT, ORD_HMS;

-- S2 시간대별 주문 수 (본사 · 하루)
SELECT SUBSTR(ORD_HMS, 1, 2) AS HH, COUNT(*) AS ORD_CNT
  FROM ORD_RANDOM
 WHERE ORD_DT = :d
 GROUP BY SUBSTR(ORD_HMS, 1, 2);

-- S3 매장 최근 주문 20건 (점장)
SELECT ORD_NO, ORD_DT, ORD_HMS
  FROM (SELECT ORD_NO, ORD_DT, ORD_HMS FROM ORD_RANDOM
         WHERE SHOP_NO = :shop
         ORDER BY ORD_DT DESC, ORD_HMS DESC)
 WHERE ROWNUM <= 20;

-- S4 월 정산 리포트 (한 달 · 자기 문장 · FULL 힌트)
SELECT /*+ FULL(o) */ o.SHOP_NO, COUNT(*) AS ORD_CNT
  FROM ORD_RANDOM o
 WHERE o.ORD_DT BETWEEN :f AND :t
 GROUP BY o.SHOP_NO;

실행계획 학습용 예시 ​

xplan
[S1 — 커서가 하루로 태어난 날 · 3개월(124,670행 중 그 매장 623행) 한 번]
-------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                |      1 |        |    623 |00:00:01.12 |    125K |
|   1 |  SORT ORDER BY                        |                |      1 |      7 |    623 |00:00:01.12 |    125K |
|*  2 |   FILTER                              |                |      1 |        |    623 |00:00:01.11 |    125K |
|*  3 |    TABLE ACCESS BY INDEX ROWID BATCHED| ORD_RANDOM     |      1 |      7 |    623 |00:00:01.11 |    125K |
|*  4 |     INDEX RANGE SCAN                  | ORD_RANDOM_X01 |      1 |   1370 |   124K |00:00:00.03 |     482 |
-------------------------------------------------------------------------------------------------------------
   2 - filter(:F<=:T)
   3 - filter("SHOP_NO"=:SHOP)
   4 - access("ORD_DT">=:F AND "ORD_DT"<=:T)

[S1 — 커서가 3개월로 태어난 날 · 하루(그 매장 7행) 한 번]
-------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                |      1 |        |      7 |00:00:00.29 |   22414 |
|   1 |  SORT ORDER BY                        |                |      1 |    623 |      7 |00:00:00.29 |   22414 |
|*  2 |   FILTER                              |                |      1 |        |      7 |00:00:00.29 |   22414 |
|*  3 |    TABLE ACCESS FULL                  | ORD_RANDOM     |      1 |    623 |      7 |00:00:00.29 |   22414 |
-------------------------------------------------------------------------------------------------------------
   3 - filter(("SHOP_NO"=:SHOP AND "ORD_DT">=:F AND "ORD_DT"<=:T))

[S2 — 어느 날이든]   INDEX RANGE SCAN ORD_RANDOM_X01 (테이블에 가지 않는다) · 하루 1,370행 · Buffers 7
[S3 — 어느 매장이든] TABLE ACCESS FULL + SORT ORDER BY STOPKEY · Buffers 22,414 · 0.3초
[S4 — 자기 문장]     TABLE ACCESS FULL · Buffers 22,414 · 한 달에 맞는 계획
[S1 — 하루로 태어난 날의 하루 한 번]  X01 + 테이블 · Buffers 1,377 (그 매장 7행을 위해 1,370행을 테이블까지 가서 거른다)

문제 ​

  1. 네 문장 각각에 대해 바인드 값이 바뀌면 맞는 계획도 바뀌는지 판단하고, S1이 날마다 다른 쪽에서 느린 이유를 설명하시오.
  2. 네 화면을 모두 보고 인덱스를 설계하고 필요한 SQL을 고치시오. 힌트를 박는 안과 문장을 나누는 안을 S1에 쓰면 어떻게 되는지도 비교하시오.
  3. 설계안의 부작용(사본 적재 시간, 인덱스 크기, 다른 문장의 계획)을 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자