Skip to content
튜닝 연습조인 튜닝
JOIN-085

힌트 유효기간 대장의 「조사필요」 세 줄을 정리하다가, 힌트를 뗀 판으로 돌린 영수증 출력이 뒤죽박죽이 됐다

실전SELECT유통235 / 700

상황 ​

본사 레거시 유통의 힌트 유효기간 대장에는 2009 ~ 2010년에 붙인 힌트 세 줄이 「조사필요」로 남아 있다. 근거 문서를 찾지 못했고 그때의 규모도 「불명」이다. 이번 정기 점검에서 이 세 줄을 닫기로 했다. 조민우가 시험 DB(운영 복제)에서 힌트가 있는 판과 뗀 판을 돌려 아래 표를 만들고 "셋 다 떼면 됩니다"라고 보고했다. 그런데 R2를 뗀 판으로 밤 배치를 시험해 보니 출력 파일의 영수증이 거의 한 줄에 한 장씩 찍혀 나왔다. 박팀장은 세 줄 모두 판정과 근거, 다음 재검토 조건을 숫자로 적어 오라고 했다.

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · CF10_PK (SLE_NO) · IX_CF10_SLE_DT (SLE_DT) · 날짜순 적재 · 하루 약 685건 · 2023년 6월 20,953건
  • SLE_NO는 'YYYYMMDD-NNNN' 모양이라 영수증 번호 순서가 곧 날짜와 발행 순서다
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) · 영수증당 평균 2.19줄 · 2015년 테이블 재구성 때 ITM_NO 순서로 다시 적재했다(물리 순서가 상품 순)
  • 영수증 출력 프로그램은 결과를 위에서부터 읽다가 SLE_NO가 바뀌면 새 영수증을 시작한다
  • 통계 최신 · SLE_DT 범위는 실제의 4분의 1 안팎으로, R2의 SLE_NO 범위(하루 685건)는 약 2,700건으로 추정된다

원본 SQL ​

sql
-- [R1] PKG_SLE_RPT.Q3 「월 상품 판매 집계」 · 2009-11 · 근거 못 찾음
SELECT /*+ LEADING(a b) USE_NL(b) */
       b.ITM_NO, COUNT(*) AS line_cnt, SUM(b.SLE_QTY) AS qty
  FROM CF10 a, CF11 b
 WHERE a.SLE_DT BETWEEN :d1 AND :d2
   AND b.SLE_NO = a.SLE_NO
 GROUP BY b.ITM_NO;

-- [R2] PKG_SLE_PRT.P1 「영수증 일괄 재출력」 (매일 밤 전날 영수증을 출력 파일로) · 2010-03 · 근거 못 찾음
SELECT /*+ ORDERED USE_NL(b) INDEX(a CF10_PK) */
       a.SLE_NO, a.SLE_DT, a.SLE_HMS, a.SHP_NO, b.ITM_NO, b.SLE_QTY, b.SLE_UPRC
  FROM CF10 a, CF11 b
 WHERE a.SLE_NO BETWEEN :d || '-0000' AND :d || '-9999'
   AND b.SLE_NO = a.SLE_NO;

-- [R3] PKG_SLE_RPT.Q5 「매장 시간대 영수증」 · 2010-08 · 근거 못 찾음
SELECT /*+ LEADING(a b) USE_NL(b) */
       a.SLE_NO, a.SLE_HMS, b.ITM_NO, b.SLE_QTY
  FROM CF10 a, CF11 b
 WHERE a.SLE_DT = :d
   AND a.SHP_NO = :shp
   AND a.SLE_HMS BETWEEN :h1 AND :h2
   AND b.SLE_NO = a.SLE_NO
 ORDER BY a.SLE_HMS, a.SLE_NO, b.ITM_NO;

실행계획 학습용 예시 ​

xplan
[재측정 · 시험 DB · Buffers (조민우)]
 줄   힌트                                  힌트 있는 판                            힌트 뗀 판
 R1   LEADING(a b) USE_NL(b)                109K · NL (CF11_PK Starts 20,953)       8,682 · HASH (CF11 FULL 한 번)
 R2   ORDERED USE_NL(b) INDEX(a CF10_PK)    3,569 · NL (CF10_PK 범위 → CF11_PK)     8,215 · HASH (CF10 하루가 build, CF11 FULL이 probe)
 R3   LEADING(a b) USE_NL(b)                122 · NL                                122 · NL (plan hash value 같음)

[R2 · 힌트 뗀 판의 출력 첫 6줄 (2023-12-24)]
 SLE_NO          SLE_HMS  ITM_NO  SLE_QTY
 20231224-0388   183512   1001    1
 20231224-0021   101204   1001    2
 20231224-0517   201733   1001    1
 20231224-0102   120945   1003    1
 20231224-0388   183512   1003    1
 20231224-0233   144410   1003    2
 → 출력 프로그램은 685장이어야 할 영수증을 1,497장으로 찍었다 (결과 1,503줄 · 힌트 있는 판은 685장)

------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
------------------------------------------------------------------------------------------------------------------------
[R1 · 힌트 있는 판 · 2023년 6월]
|   0 | SELECT STATEMENT                       |                |      1 |        |    109 |00:00:00.62 |    109K |          |
|   1 |  HASH GROUP BY                         |                |      1 |    109 |    109 |00:00:00.62 |    109K | 1210K (0)|
|   2 |   NESTED LOOPS                         |                |      1 |  11337 |  45887 |00:00:00.60 |    109K |          |
|   3 |    NESTED LOOPS                        |                |      1 |  11337 |  45887 |00:00:00.30 |   63337 |          |
|   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| CF10           |      1 |   5177 |  20953 |00:00:00.02 |     478 |          |
|*  5 |      INDEX RANGE SCAN                  | IX_CF10_SLE_DT |      1 |   5177 |  20953 |00:00:00.01 |      60 |          |
|*  6 |     INDEX RANGE SCAN                   | CF11_PK        |  20953 |      2 |  45887 |00:00:00.21 |   62859 |          |
|   7 |    TABLE ACCESS BY INDEX ROWID         | CF11           |  45887 |      2 |  45887 |00:00:00.27 |   45885 |          |
------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access("A"."SLE_DT">=:D1 AND "A"."SLE_DT"<=:D2)
   6 - access("B"."SLE_NO"="A"."SLE_NO")

문제 ​

  1. 세 줄을 각각 판정(폐기, 유지, 재검토, 조사필요)하고 그 근거를 재측정값으로 쓰시오.
  2. R2를 그냥 폐기하면 무엇이 깨지는지 출력 결과로 설명하고, 순서와 성능을 모두 지키도록 SQL을 고치시오.
  3. 세 줄의 다음 재검토 조건을 이 조인의 손익분기점을 근거로 숫자로 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자