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

점장이 영수증 한 장을 찾는 데 판매상품 110만 줄을 다 읽는다

중급SELECT유통258 / 700

상황 ​

매장 POS의 「영수증 찾기」는 손님이 "어제 산 아메리카노 영수증"을 찾을 때 쓴다. 점장이 날짜와 상품을 넣으면 그 매장의 그날 영수증 가운데 그 상품이 든 것만 보여 준다. 다섯 매장에서 하루 수백 번 쓰는데 매장 PC에서 0.1초 가까이 걸린다. 서브쿼리에는 /*+ UNNEST HASH_SJ */가 붙어 있고, 옆에 주석 -- 서브쿼리는 해시 세미 조인으로 (2021 월 판매 집계 개선 때 정한 규칙)이 있다.

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · CF10_PK (SLE_NO) · IX_CF10_SLE_DT (SLE_DT) (03화에서 만든 인덱스, 하루치가 약 14블록에 모여 있다) · 판매 매장은 1001 ~ 1005의 5곳
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) (높이 3) · ITM_NO로 시작하는 인덱스는 없다
  • 강남점(1001)의 2023-06-01 영수증 150장 · 상품 :v_itm(아메리카노)은 2년 치 판매 줄이 41,280줄이고, 그날 강남점 영수증 중 23장에 들어 있다
  • 통계 최신

원본 SQL ​

sql
SELECT a.SLE_NO, a.SLE_HMS
  FROM CF10 a
 WHERE a.SLE_DT = :v_dt
   AND a.SHP_NO = :v_shp
   AND EXISTS (SELECT /*+ UNNEST HASH_SJ */ 1   -- 서브쿼리는 해시 세미 조인으로 (2021 월 판매 집계 개선 때 정한 규칙)
                 FROM CF11 b
                WHERE b.SLE_NO = a.SLE_NO
                  AND b.ITM_NO = :v_itm)
 ORDER BY a.SLE_NO

실행계획 학습용 예시 · 행 수는 실데이터 규모 ​

xplan
----------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                |      1 |        |     23 |00:00:00.08 |    8223 |
|   1 |  SORT ORDER BY                        |                |      1 |     12 |     23 |00:00:00.08 |    8223 |
|*  2 |   HASH JOIN SEMI                      |                |      1 |     12 |     23 |00:00:00.08 |    8223 |
|*  3 |    TABLE ACCESS BY INDEX ROWID BATCHED| CF10           |      1 |    140 |    150 |00:00:00.01 |      19 |
|*  4 |     INDEX RANGE SCAN                  | IX_CF10_SLE_DT |      1 |    700 |    706 |00:00:00.01 |       5 |
|*  5 |    TABLE ACCESS FULL                  | CF11           |      1 |  12463 |  41280 |00:00:00.07 |    8204 |
----------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("B"."SLE_NO"="A"."SLE_NO")
   3 - filter("A"."SHP_NO"=:V_SHP)
   4 - access("A"."SLE_DT"=:V_DT)
   5 - filter("B"."ITM_NO"=:V_ITM)

문제 ​

  1. 5번 줄의 8,204블록이 무엇을 읽은 것인지, 이 화면에 필요한 일과 비교해 설명하시오.
  2. 결과가 바뀌지 않게 영수증 150장만큼만 일하도록 SQL을 고치시오. 2021년의 규칙이 이 화면에 맞지 않는 이유도 쓰시오.
  3. 서브쿼리를 먼저 읽고(드라이빙) 판매 쪽을 찾아가는 모양으로 바꾸면 어떻게 될지 숫자로 어림하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자