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

하루 주문 1,368건의 품목 수를 붙이는 마감 점검이 주문상품 인덱스를 1,368번 찾아간다

중급SELECT버거285 / 700

상황 ​

버거 본사의 「일 마감 주문 점검」은 날짜를 고르면 그날 전 매장 주문을 주문별 품목 수와 수량 합계와 함께 보여 준다. 마감 담당이 매일 밤 여러 번 열고, 감사팀은 지난 날짜를 하루에 수백 번 넘겨 본다. DBA가 보니 실행계획에 VIEW PUSHED PREDICATE가 있었다. 오과장은 "조인 조건을 뷰 안으로 밀어 넣는 좋은 변환이라던데 5,500블록이나 읽는다. 그날 주문상품은 3,381줄뿐이다"라고 한다.

테이블과 인덱스 ​

  • ORD 주문: 1,000,000행 · 22,422블록 · ORD_X01 (ORD_DT, ORD_HMS): 날짜순 적재 · 하루치 약 1,370건이 31블록에 모여 있다
  • ORD_ITEM 주문상품: 2,472,717행 · 55,265블록 · ORD_ITEM_PK (ORD_NO, ITEM_ID) 높이 3 · ORD_ITEM_X01 (ORD_DT, ORD_HMS) · 주문번호 순(곧 날짜순)으로 쌓여 하루치가 약 76블록에 모여 있다 · 한 주문의 상품은 한 블록에 있다
  • ORD_ITEM의 ORD_DT와 ORD_HMS는 주문 헤더의 값을 그대로 복사한 컬럼이다(적재 프로그램이 함께 넣는다)
  • 2013-03-15 주문 1,368건 · 주문상품 3,381줄 · 통계 최신

원본 SQL ​

sql
SELECT o.ORD_NO, o.SHOP_NO, o.ORD_HMS, v.ITEM_CNT, v.QTY
  FROM ORD o,
       (SELECT ORD_NO, COUNT(*) AS ITEM_CNT, SUM(ORD_ITEM_QTY) AS QTY
          FROM ORD_ITEM
         GROUP BY ORD_NO) v
 WHERE o.ORD_DT = :dt
   AND v.ORD_NO = o.ORD_NO
 ORDER BY o.SHOP_NO, o.ORD_HMS

실행계획 학습용 예시 ​

xplan
----------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
----------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |             |      1 |        |   1368 |00:00:00.05 |    5509 |          |
|   1 |  SORT ORDER BY                        |             |      1 |   1370 |   1368 |00:00:00.05 |    5509 |  124K (0)|
|   2 |   NESTED LOOPS                        |             |      1 |   1370 |   1368 |00:00:00.05 |    5509 |          |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| ORD         |      1 |   1370 |   1368 |00:00:00.01 |      37 |          |
|*  4 |     INDEX RANGE SCAN                  | ORD_X01     |      1 |   1370 |   1368 |00:00:00.01 |       6 |          |
|   5 |    VIEW PUSHED PREDICATE              |             |   1368 |      1 |   1368 |00:00:00.04 |    5472 |          |
|   6 |     SORT GROUP BY                     |             |   1368 |      1 |   1368 |00:00:00.03 |    5472 |          |
|   7 |      TABLE ACCESS BY INDEX ROWID      | ORD_ITEM    |   1368 |      2 |   3381 |00:00:00.02 |    5472 |          |
|*  8 |       INDEX RANGE SCAN                | ORD_ITEM_PK |   1368 |      2 |   3381 |00:00:00.01 |    4104 |          |
----------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   4 - access("O"."ORD_DT"=:DT)
   8 - access("ORD_NO"="O"."ORD_NO")

문제 ​

  1. 5번부터 8번 줄이 무엇을 몇 번 했는지, 5,472블록이 어떻게 나오는지 설명하시오.
  2. 결과는 그대로 두고, 주문상품을 그날 범위만 한 번에 읽도록 SQL을 고치시오. 날짜 조건이 뷰 안으로 저절로 들어가지 않은 이유도 쓰시오.
  3. 고친 방식보다 지금의 VIEW PUSHED PREDICATE가 나은 경우를 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자