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

코드 정리 배포 다음 날부터 매장 「영수증 목록」 첫 화면이 0.01초에서 0.15초로 늘었다

기초SELECT유통223 / 700

상황 ​

매장 관리 화면의 「영수증 목록」은 매장 하나와 기간(기본값 최근 3개월)을 받아 가장 최근 영수증부터 상품 줄을 보여 준다. 화면은 커서를 열어 20줄을 받아 그리고, 「다음」을 눌러야 이어서 받는다. 점장들과 본사 영업팀이 하루 3천 번쯤 누른다. 2009년에 만든 SQL은 별칭 없이 테이블 이름만 썼다. 지난주 코드 정리 배포에서 "모든 테이블에 별칭을 붙인다"는 새 코딩 규칙에 맞춰 SQL 수백 개의 FROM 절과 컬럼 이름을 도구로 일괄 고쳤다. 그다음 날부터 이 화면의 첫 화면이 0.15초씩 걸린다. 아래는 매장 1003의 2023-10-01 ~ 2023-12-31을 조회한 지금의 실행계획이다.

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · 날짜순 적재 · 매장 1003의 2023년 4분기 영수증 10,476건
  • 인덱스 IX_CF10_SHP_DT_HMS (SHP_NO, SLE_DT, SLE_HMS)
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) · 영수증당 평균 2.19줄
  • 통계 최신 · 옵티마이저 모드 ALL_ROWS(기본값) · SLE_DT가 VARCHAR2(8)이라 옵티마이저는 이 범위를 약 2,600건으로 본다(교재 03화)
  • 배포 전 SQL은 FROM CF10, CF11이었고 힌트는 지금과 글자까지 같았다. 그때 첫 화면은 63블록 · 0.01초였다

원본 SQL ​

sql
SELECT /*+ LEADING(CF10 CF11) USE_NL(CF11) INDEX_DESC(CF10 IX_CF10_SHP_DT_HMS) */
       a.SLE_DT, a.SLE_HMS, a.SLE_NO, b.ITM_NO, b.SLE_QTY, b.SLE_UPRC
  FROM CF10 a, CF11 b
 WHERE a.SHP_NO = :shp
   AND a.SLE_DT BETWEEN :d1 AND :d2
   AND b.SLE_NO = a.SLE_NO
 ORDER BY a.SLE_DT DESC, a.SLE_HMS DESC

실행계획 학습용 예시 ​

xplan
------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name               | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                    |      1 |        |     20 |00:00:00.15 |    9506 |          |
|   1 |  SORT ORDER BY                        |                    |      1 |   5694 |     20 |00:00:00.15 |    9506 | 1870K (0)|
|*  2 |   HASH JOIN                           |                    |      1 |   5694 |  22942 |00:00:00.12 |    9506 | 1330K (0)|
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| CF10               |      1 |   2600 |  10476 |00:00:00.01 |    1302 |          |
|*  4 |     INDEX RANGE SCAN                  | IX_CF10_SHP_DT_HMS |      1 |   2600 |  10476 |00:00:00.01 |      42 |          |
|   5 |    TABLE ACCESS FULL                  | CF11               |      1 |  1096K |  1096K |00:00:00.08 |    8204 |          |
------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("B"."SLE_NO"="A"."SLE_NO")
   4 - access("A"."SHP_NO"=:SHP AND "A"."SLE_DT">=:D1 AND "A"."SLE_DT"<=:D2)

Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 3 (N - Unresolved (3))
---------------------------------------------------------------------------

   1 -  SEL$1
         N -  INDEX_DESC(CF10 IX_CF10_SHP_DT_HMS)
         N -  LEADING(CF10 CF11)
         N -  USE_NL(CF11)

문제 ​

  1. 힌트가 그대로인데 계획이 바뀐 이유를 Hint Report와 실행계획으로 설명하시오. 힌트가 없을 때 옵티마이저가 왜 이 계획을 골랐는지도 쓰시오.
  2. 첫 화면이 다시 배포 전처럼 처리되도록 SQL을 고치시오. 같은 일이 되풀이되지 않게 하려면 무엇을 해야 하는지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자