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

리콜 추적용 개수 단위 이력이 주문상품 수량 합계보다 2,835개 적다

중급INSERT버거301 / 700

상황 ​

버거 품질팀은 리콜에 대비해 주문 상품을 개수 단위로 한 줄씩 남긴다(ITEM_UNIT_TRC). 수량 3개짜리 주문상품은 1번, 2번, 3번 세 줄이 된다. 지난주 원천 정정으로 2013년 12월 한 달을 다시 만들었는데, 품질팀이 "12월 이력이 주문상품 수량 합계보다 2,835개 적다"라고 알려 왔다. 적재는 3초가 걸린다. 적재 SQL은 5년 전 "수량은 많아야 5개"라는 말을 듣고 짠 것이다. 지금은 단체 주문이 늘어 한 줄 수량이 수십 개인 주문상품도 있다.

테이블과 인덱스 ​

  • ORD_ITEM 주문상품: 2,472,717행 · 55,265블록 · ORD_NO · ITEM_ID · ORD_ITEM_QTY · ORD_DT · ORD_HMS · PK (ORD_NO, ITEM_ID) · ORD_ITEM_X01 (ORD_DT, ORD_HMS): 날짜순으로 쌓여 있어 한 달 범위를 읽기 좋다
  • 2013년 12월: 104,860줄 · 수량 합계 150,688개 · 수량 6개 이상인 줄 315개(최대 40개) · POS는 한 줄 수량을 99개까지만 받는다
  • ITEM_UNIT_TRC 개수 단위 이력 (이 문제에서 쓰는 파생 테이블): ORD_NO · ITEM_ID · UNIT_SEQ · ORD_DT · PK (ORD_NO, ITEM_ID, UNIT_SEQ) · 다시 만들 달은 먼저 지운다 · 야간에만 적재한다
  • 통계 최신 · 화면은 그달 1일과 말일(:d1, :d2, 'YYYYMMDD')을 넘긴다

원본 SQL ​

sql
INSERT INTO ITEM_UNIT_TRC (ORD_NO, ITEM_ID, UNIT_SEQ, ORD_DT)
SELECT ORD_NO, ITEM_ID, 1, ORD_DT FROM ORD_ITEM
 WHERE ORD_DT BETWEEN :d1 AND :d2 AND ORD_ITEM_QTY >= 1
UNION ALL
SELECT ORD_NO, ITEM_ID, 2, ORD_DT FROM ORD_ITEM
 WHERE ORD_DT BETWEEN :d1 AND :d2 AND ORD_ITEM_QTY >= 2
UNION ALL
SELECT ORD_NO, ITEM_ID, 3, ORD_DT FROM ORD_ITEM
 WHERE ORD_DT BETWEEN :d1 AND :d2 AND ORD_ITEM_QTY >= 3
UNION ALL
SELECT ORD_NO, ITEM_ID, 4, ORD_DT FROM ORD_ITEM
 WHERE ORD_DT BETWEEN :d1 AND :d2 AND ORD_ITEM_QTY >= 4
UNION ALL
SELECT ORD_NO, ITEM_ID, 5, ORD_DT FROM ORD_ITEM
 WHERE ORD_DT BETWEEN :d1 AND :d2 AND ORD_ITEM_QTY >= 5

실행계획 학습용 예시 ​

xplan
[INSERT 안의 SELECT만 떼어 돌린 계획 · 2013년 12월]
------------------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name         | Starts | E-Rows | A-Rows |      A-Time | Buffers |
------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |              |      1 |        | 147853 | 00:00:00.29 |   13450 |
|   1 |  UNION-ALL                             |              |      1 |        | 147853 | 00:00:00.29 |   13450 |
|*  2 |   TABLE ACCESS BY INDEX ROWID BATCHED  | ORD_ITEM     |      1 |   103K | 104860 | 00:00:00.07 |    2690 |
|*  3 |    INDEX RANGE SCAN                    | ORD_ITEM_X01 |      1 |   103K | 104860 | 00:00:00.01 |     346 |
|*  4 |   TABLE ACCESS BY INDEX ROWID BATCHED  | ORD_ITEM     |      1 |    29K |  29361 | 00:00:00.06 |    2690 |
|*  5 |    INDEX RANGE SCAN                    | ORD_ITEM_X01 |      1 |   103K | 104860 | 00:00:00.01 |     346 |
|*  6 |   TABLE ACCESS BY INDEX ROWID BATCHED  | ORD_ITEM     |      1 |   9300 |   9437 | 00:00:00.06 |    2690 |
|*  7 |    INDEX RANGE SCAN                    | ORD_ITEM_X01 |      1 |   103K | 104860 | 00:00:00.01 |     346 |
|*  8 |   TABLE ACCESS BY INDEX ROWID BATCHED  | ORD_ITEM     |      1 |   3100 |   3146 | 00:00:00.05 |    2690 |
|*  9 |    INDEX RANGE SCAN                    | ORD_ITEM_X01 |      1 |   103K | 104860 | 00:00:00.01 |     346 |
|* 10 |   TABLE ACCESS BY INDEX ROWID BATCHED  | ORD_ITEM     |      1 |   1030 |   1049 | 00:00:00.05 |    2690 |
|* 11 |    INDEX RANGE SCAN                    | ORD_ITEM_X01 |      1 |   103K | 104860 | 00:00:00.01 |     346 |
------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("ORD_ITEM_QTY">=1)
   3 - access("ORD_DT">=:D1 AND "ORD_DT"<=:D2)
   4 - filter("ORD_ITEM_QTY">=2)
   5 - access("ORD_DT">=:D1 AND "ORD_DT"<=:D2)
   (6 ~ 11번 줄도 같은 모양 · 수량 조건만 3, 4, 5)

[적재 전체]  3.02초 · 이력 147,853행 (SELECT 0.29초 + 테이블과 PK 인덱스에 넣는 일 2.73초)

문제 ​

  1. 이 적재가 같은 주문상품을 몇 번 읽는지 실행계획의 줄 번호로 설명하고, 이력이 2,835개 모자란 이유를 설명하시오.
  2. 주문상품을 한 번만 읽고 수량만큼 행을 만드는 INSERT로 고치시오. 복제할 번호의 상한을 어떻게 정하고 지킬지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자