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

교환 손님이 올 때마다 POS의 「영수증 이력」 창이 0.2초씩 멈춘다

기초SELECT유통300 / 700

상황 ​

유통 매장 POS의 「영수증 이력」 창은 원래 영수증 한 장에서 이어진 반품 영수증과 교환 영수증을 모두 펼쳐 보여 준다. 교환한 상품을 다시 교환하는 손님도 있어서 이력은 여러 단계가 된다. 반품, 교환, 재출력 때마다 POS가 이 창을 저절로 열어 다섯 매장에서 하루 약 2,000번 돈다. 지난 분기에 반품 영수증과 교환 영수증이 원래 영수증을 가리키도록 CF10에 ORG_SLE_NO 컬럼을 더했다. 그 뒤로 창이 열릴 때마다 0.2초씩 멈춘다는 말이 매장에서 올라왔다. 아래는 2023-12-03 강남점 영수증의 이력을 펼친 것이다.

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · CF10_PK (SLE_NO)(높이 3) · ORG_SLE_NO VARCHAR2(15): 반품·교환 영수증이 가리키는 원래 영수증 번호 · 값이 있는 행은 반품·교환 영수증 4,860장뿐이고 나머지는 NULL(뒤쪽 컬럼이 NULL이라 블록 수는 거의 그대로다) (문제 설정: 교재의 CF10에는 이 컬럼이 없다. 반품 처리를 위해 운영 중에 더한 컬럼이라고 본다)
  • ORG_SLE_NO에는 인덱스가 없다 · 이력은 대부분 2단계이고 길어야 4단계다 · 통계 최신
  • 아래 영수증의 이력: 원래 영수증 1장 → 12/05 부분 반품 1장과 교환 판매 1장 → 12/09 교환 판매의 재교환 1장 → 12/15 그 반품 1장 (5장, 4단계)

원본 SQL ​

sql
SELECT LEVEL AS lvl, SLE_NO, ORG_SLE_NO, SLE_DT, SLE_HMS, SHP_NO
  FROM CF10
 START WITH SLE_NO = :v_sle_no
CONNECT BY PRIOR SLE_NO = ORG_SLE_NO

실행계획 학습용 예시 ​

xplan
--------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
--------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |      1 |        |      5 |00:00:00.21 |   18336 |          |
|*  1 |  CONNECT BY WITH FILTERING   |         |      1 |        |      5 |00:00:00.21 |   18336 | 2048  (0)|
|   2 |   TABLE ACCESS BY INDEX ROWID| CF10    |      1 |      1 |      1 |00:00:00.01 |       4 |          |
|*  3 |    INDEX UNIQUE SCAN         | CF10_PK |      1 |      1 |      1 |00:00:00.01 |       3 |          |
|*  4 |   HASH JOIN                  |         |      4 |      2 |      4 |00:00:00.21 |   18332 | 1236K (0)|
|   5 |    CONNECT BY PUMP           |         |      4 |        |      5 |00:00:00.01 |       0 |          |
|   6 |    TABLE ACCESS FULL         | CF10    |      4 |    500K|   2000K|00:00:00.12 |   18332 |          |
--------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("ORG_SLE_NO"=PRIOR "SLE_NO")
   3 - access("SLE_NO"=:V_SLE_NO)
   4 - access("connect$_by$_pump$_002"."prior sle_no"="ORG_SLE_NO")

문제 ​

  1. 4번과 6번 줄의 Starts 4가 무엇의 횟수인지 쓰고, 영수증 다섯 장의 이력을 펼치는 데 18,336블록을 읽은 이유를 설명하시오.
  2. 인덱스를 설계하고(DDL) 그 인덱스가 50만 행 테이블에 비해 아주 작은 이유를 쓰시오. 같은 이력을 재귀 WITH로 쓰면 인덱스 없이 해결되는지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자