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

버거와 감자튀김과 콜라를 낱개로 함께 산 주문을 세는 데 주문상품 PK를 1만 3천 번 찾아간다

기초SELECT버거213 / 700

상황 ​

본사 마케팅팀은 세트 메뉴 판촉을 위해 「세트 전환 가능 주문」을 날마다 센다. 불고기버거와 감자튀김과 콜라를 세트가 아니라 낱개로 함께 산 주문이 하루에 몇 건인지 보는 표다. 메뉴 세 개를 바꿔 가며 하루 200번쯤 돌린다. SQL은 주문상품 테이블을 메뉴마다 한 번씩, 세 번 조인한다. 아래는 2013년 12월로 조회한 실행계획이다.

테이블과 인덱스 ​

  • ORD_ITEM 주문상품: 2,472,717행 · 55,265블록 · ORD_ITEM_PK (ORD_NO, ITEM_ID) · ORD_ITEM_X01 (ORD_DT, ORD_HMS)(날짜순 적재, CF 좋음) · 둘 다 BLEVEL 2
  • 2013년 12월 주문상품 104,750줄(X01 리프 약 390블록, 테이블 약 2,340블록에 모여 있다)
  • 12월 불고기버거(ITEM_ID = 3) 8,931줄 · 감자튀김(52) 7,104줄 · 콜라(71) 5,669줄 · 버거와 감자가 함께 든 주문 4,102건 · 셋 다 든 주문 2,267건
  • 같은 주문에 같은 메뉴는 한 줄뿐이다(여러 개면 ORD_ITEM_QTY가 커진다) · ORD_DT는 주문 헤더에서 복사한 값이라 한 주문 안에서 같다 · ITEM_ID에 히스토그램 없음 · 통계 최신

원본 SQL ​

sql
SELECT a.ORD_DT, COUNT(*) AS combo_ord
  FROM ORD_ITEM a, ORD_ITEM b, ORD_ITEM c
 WHERE a.ORD_DT BETWEEN :v_from AND :v_to
   AND a.ITEM_ID = :v_burger
   AND b.ORD_NO  = a.ORD_NO
   AND b.ITEM_ID = :v_fries
   AND c.ORD_NO  = a.ORD_NO
   AND c.ITEM_ID = :v_coke
 GROUP BY a.ORD_DT
 ORDER BY a.ORD_DT

실행계획 학습용 예시 ​

xplan
------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name         | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |              |      1 |        |     31 |00:00:00.19 |   42217 |          |
|   1 |  SORT GROUP BY                           |              |      1 |     31 |     31 |00:00:00.19 |   42217 | 2048 (0) |
|   2 |   NESTED LOOPS                           |              |      1 |      1 |   2267 |00:00:00.19 |   42217 |          |
|   3 |    NESTED LOOPS                          |              |      1 |     31 |   4102 |00:00:00.14 |   29911 |          |
|*  4 |     TABLE ACCESS BY INDEX ROWID BATCHED  | ORD_ITEM     |      1 |   1139 |   8931 |00:00:00.04 |    2729 |          |
|*  5 |      INDEX RANGE SCAN                    | ORD_ITEM_X01 |      1 |   104K |   104K |00:00:00.01 |     392 |          |
|*  6 |     INDEX UNIQUE SCAN                    | ORD_ITEM_PK  |   8931 |      1 |   4102 |00:00:00.09 |   27182 |          |
|*  7 |    INDEX UNIQUE SCAN                     | ORD_ITEM_PK  |   4102 |      1 |   2267 |00:00:00.04 |   12306 |          |
------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   4 - filter("A"."ITEM_ID"=:V_BURGER)
   5 - access("A"."ORD_DT">=:V_FROM AND "A"."ORD_DT"<=:V_TO)
   6 - access("B"."ORD_NO"="A"."ORD_NO" AND "B"."ITEM_ID"=:V_FRIES)
   7 - access("C"."ORD_NO"="A"."ORD_NO" AND "C"."ITEM_ID"=:V_COKE)

문제 ​

  1. 6번 줄과 7번 줄의 Starts를 보고, 같은 테이블을 세 번 조인하는 이 SQL의 일량이 어디서 나오는지 설명하시오.
  2. 주문상품을 한 번만 읽도록 SQL을 고치시오. 고친 SQL이 원래 SQL과 같은 결과를 내는 근거도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자