테마
버거 본사는 주문상품도 주문과 같은 기준의 파티션 복사본 ORD_ITEM_RANGE로 옮겼다. 그 뒤 주문과 주문상품을 조인하는 세 SQL이 느려졌다. ① 「월간 매장별 메뉴 판매」는 월초에 본사가 수십 번 돌리고, ② POS 「영수증 상세」는 하루 약 6만 번, ③ 「매장 월 주문상품 내려받기」는 하루 약 400번 호출된다.
세 SQL 모두 ORD_RANGE 쪽은 한 칸만 연다. 아래 ①과 ③은 2013년 6월(③은 매장 SH0042), ②는 2013년 7월 주문 777777로 돌린 결과다.
ORD_RANGE 주문(파티션 복사본, 교재 14화): 1,000,000행 · PARTITION BY RANGE (ORD_YM) 월별 25칸(P201201 ~ P201312과 빈 P_DEFAULT). 칸 하나는 FULL로 약 1,980블록이다. 한 달 약 41,000건이고, 매장 200개라서 매장 하나의 한 달 주문은 약 205건이다.ORD_ITEM_RANGE 주문상품(파티션 복사본): ORD_ITEM 2,472,717행을 같은 기준(PARTITION BY RANGE (ORD_YM), 25칸)으로 나눴다. 칸 하나는 약 103,000행이고 FULL로 약 3,310블록(데이터 약 2,300블록과 빈 블록 약 1,006블록), 24칸 전체는 약 79,400블록이다. 주문 한 건에 상품은 약 2.5건이다.ORD_YM은 늘 그 주문의 ORD_YM과 같다(적재할 때 주문에서 가져왔다).ORD_RANGE_PK (ORD_NO, ORD_YM), ORD_ITEM_RANGE_PK (ORD_NO, ITEM_ID, ORD_YM). 두 인덱스 모두 칸마다 높이 2다.DROP PARTITION으로 정리한다. 통계는 최신이다.-- ① 월간 매장별 메뉴 판매
SELECT o.SHOP_NO, i.ITEM_ID, SUM(i.ORD_ITEM_QTY) AS qty
FROM ORD_RANGE o, ORD_ITEM_RANGE i
WHERE o.ORD_YM = :v_ym
AND i.ORD_NO = o.ORD_NO
GROUP BY o.SHOP_NO, i.ITEM_ID;
-- ② 영수증 상세
SELECT o.ORD_NO, o.ORD_DT, o.ORD_HMS, i.ITEM_ID, i.ORD_ITEM_QTY
FROM ORD_RANGE o, ORD_ITEM_RANGE i
WHERE o.ORD_NO = :ord_no
AND o.ORD_YM = :ord_ym
AND i.ORD_NO = o.ORD_NO;
-- ③ 매장 월 주문상품 내려받기
SELECT i.ORD_NO, i.ITEM_ID, i.ORD_ITEM_QTY
FROM ORD_ITEM_RANGE i
WHERE i.ORD_NO IN (SELECT o.ORD_NO
FROM ORD_RANGE o
WHERE o.ORD_YM = :v_ym
AND o.SHOP_NO = :v_shop);
[① 월간 매장별 메뉴 판매 — :v_ym = '201306']
------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Pstart | Pstop |
------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 18K | 00:00:01.92 | 81396 | | |
| 1 | HASH GROUP BY | | 1 | 18K | 18K | 00:00:01.92 | 81396 | | |
|* 2 | HASH JOIN | | 1 | 103K | 102K | 00:00:01.74 | 81396 | | |
| 3 | PARTITION RANGE SINGLE | | 1 | 41K | 41K | 00:00:00.04 | 1980 | KEY | KEY |
|* 4 | TABLE ACCESS FULL | ORD_RANGE | 1 | 41K | 41K | 00:00:00.03 | 1980 | KEY | KEY |
| 5 | PARTITION RANGE ALL | | 1 | 2472K | 2472K | 00:00:00.95 | 79416 | 1 | 25 |
| 6 | TABLE ACCESS FULL | ORD_ITEM_RANGE | 24 | 2472K | 2472K | 00:00:00.71 | 79416 | 1 | 25 |
------------------------------------------------------------------------------------------------------------------------
2 - access("I"."ORD_NO"="O"."ORD_NO")
4 - filter("O"."ORD_YM"=:V_YM)
[② 영수증 상세 — :ord_no = 777777, :ord_ym = '201307']
--------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Pstart | Pstop |
--------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 3 | 00:00:00.01 | 55 | | |
| 1 | NESTED LOOPS | | 1 | 2 | 3 | 00:00:00.01 | 55 | | |
| 2 | PARTITION RANGE SINGLE | | 1 | 1 | 1 | 00:00:00.01 | 3 | KEY | KEY |
| 3 | TABLE ACCESS BY LOCAL INDEX ROWID | ORD_RANGE | 1 | 1 | 1 | 00:00:00.01 | 3 | KEY | KEY |
|* 4 | INDEX UNIQUE SCAN | ORD_RANGE_PK | 1 | 1 | 1 | 00:00:00.01 | 2 | KEY | KEY |
| 5 | PARTITION RANGE ALL | | 1 | 2 | 3 | 00:00:00.01 | 52 | 1 | 25 |
| 6 | TABLE ACCESS BY LOCAL INDEX ROWID | ORD_ITEM_RANGE | 24 | 2 | 3 | 00:00:00.01 | 52 | 1 | 25 |
|* 7 | INDEX RANGE SCAN | ORD_ITEM_RANGE_PK | 24 | 2 | 3 | 00:00:00.01 | 49 | 1 | 25 |
--------------------------------------------------------------------------------------------------------------------------------------
4 - access("O"."ORD_NO"=:ORD_NO AND "O"."ORD_YM"=:ORD_YM)
7 - access("I"."ORD_NO"=:ORD_NO)
[③ 매장 월 주문상품 내려받기 — :v_ym = '201306', :v_shop = 'SH0042']
--------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Pstart | Pstop |
--------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 512 | 00:00:00.07 | 12348 | | |
| 1 | NESTED LOOPS | | 1 | 513 | 512 | 00:00:00.07 | 12348 | | |
| 2 | SORT UNIQUE | | 1 | 205 | 205 | 00:00:00.03 | 1980 | | |
| 3 | PARTITION RANGE SINGLE | | 1 | 205 | 205 | 00:00:00.03 | 1980 | KEY | KEY |
|* 4 | TABLE ACCESS FULL | ORD_RANGE | 1 | 205 | 205 | 00:00:00.03 | 1980 | KEY | KEY |
| 5 | PARTITION RANGE ALL | | 205 | 3 | 512 | 00:00:00.04 | 10368 | 1 | 25 |
| 6 | TABLE ACCESS BY LOCAL INDEX ROWID | ORD_ITEM_RANGE | 4920 | 3 | 512 | 00:00:00.04 | 10368 | 1 | 25 |
|* 7 | INDEX RANGE SCAN | ORD_ITEM_RANGE_PK | 4920 | 3 | 512 | 00:00:00.03 | 9856 | 1 | 25 |
--------------------------------------------------------------------------------------------------------------------------------------
4 - filter(("O"."SHOP_NO"=:V_SHOP AND "O"."ORD_YM"=:V_YM))
7 - access("I"."ORD_NO"="O"."ORD_NO")
ORD_ITEM_RANGE가 몇 칸을 여는지 Pstart, Pstop과 Starts로 쓰고, ORD_RANGE는 한 칸인데 주문상품 쪽은 칸을 고르지 못하는 까닭을 설명하시오.ORD_ITEM_RANGE에 ORD_NO 하나짜리 GLOBAL 인덱스를 만드는 안과 비교해, 세 SQL에서 무엇이 달라지는지와 그 대가를 쓰시오.