테마
버거 본사 「주문 조회」 화면은 기간(필수, 최대 7일)에 매장, 메뉴, 시간대를 옵션으로 더해 주문을 찾는다. SQL은 한 문장이고 옵션마다 (:v IS NULL OR ...)를 달았다. 메뉴 조건은 주문상품에 그 메뉴가 있는지 EXISTS로 확인한다.
강도현이 한 달 치 호출 로그를 세어 보니 네 조합이 거의 전부였다. C1 기간 + 매장(하루 약 1,200번), C2 기간 + 매장 + 시간대(약 500번), C3 기간 + 메뉴(약 200번), C4 기간 + 시간대(약 100번)다. 박팀장은 "조합마다 SQL을 어떻게 나누고 인덱스를 몇 개 둘지, POS 주문 적재가 얼마나 무거워지는지까지 적어 와라"고 했다.
ORD 주문: 1,000,000행 · 22,422블록 · ORD_NO(PK) · ORD_DT VARCHAR2(8) · ORD_HMS VARCHAR2(6) · SHOP_NO(매장 200곳) · 주문 일자 순으로 쌓였다 · 하루 평균 1,368건, 7일 약 9,576건, 매장 하나의 7일 약 48건ORD_ITEM 주문상품: 2,472,717행 · 55,265블록 · ORD_NO · ITEM_ID(메뉴 92종) · ORD_ITEM_QTY · ORD_DT · ORD_HMS(주문의 값을 그대로 복사해 둔 컬럼) · 주문 하나에 평균 2.5줄 · 메뉴 하나의 7일 약 260줄ORD_PK (ORD_NO) · ORD_X01 (ORD_DT, ORD_HMS) · ORD_ITEM_PK (ORD_NO, ITEM_ID) · ORD_ITEM_X01 (ORD_DT, ORD_HMS) · 모두 BLEVEL 2 · 통계 최신SELECT o.ORD_NO, o.ORD_DT, o.ORD_HMS, o.SHOP_NO
FROM ORD o
WHERE o.ORD_DT BETWEEN :v_from AND :v_to
AND (:v_shop IS NULL OR o.SHOP_NO = :v_shop)
AND (:v_hms_from IS NULL OR o.ORD_HMS BETWEEN :v_hms_from AND :v_hms_to)
AND (:v_item IS NULL OR EXISTS (SELECT 1
FROM ORD_ITEM i
WHERE i.ORD_NO = o.ORD_NO
AND i.ITEM_ID = :v_item))
ORDER BY o.ORD_DT, o.ORD_HMS
[C1 · 매장 SH0042 · 2013-03-11 ~ 2013-03-17]
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 47 | 00:00:00.03 | 251 |
|* 1 | FILTER | | 1 | | 47 | 00:00:00.03 | 251 |
|* 2 | TABLE ACCESS BY INDEX ROWID | ORD | 1 | 48 | 47 | 00:00:00.03 | 251 |
|* 3 | INDEX RANGE SCAN | ORD_X01 | 1 | 9576 | 9576 | 00:00:00.01 | 36 |
|* 4 | INDEX UNIQUE SCAN | ORD_ITEM_PK | 0 | 1 | 0 | 00:00:00.01 | 0 |
--------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter((:V_ITEM IS NULL OR IS NOT NULL))
2 - filter((:V_SHOP IS NULL OR "O"."SHOP_NO"=:V_SHOP))
3 - access("O"."ORD_DT">=:V_FROM AND "O"."ORD_DT"<=:V_TO)
filter((:V_HMS_FROM IS NULL OR ("O"."ORD_HMS">=:V_HMS_FROM AND "O"."ORD_HMS"<=:V_HMS_TO)))
4 - access("I"."ORD_NO"=:B1 AND "I"."ITEM_ID"=:V_ITEM)
[C3 · 메뉴 23 · 같은 7일 · 같은 커서]
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 261 | 00:00:00.21 | 28979 |
|* 1 | FILTER | | 1 | | 261 | 00:00:00.21 | 28979 |
|* 2 | TABLE ACCESS BY INDEX ROWID | ORD | 1 | 48 | 9576 | 00:00:00.03 | 251 |
|* 3 | INDEX RANGE SCAN | ORD_X01 | 1 | 9576 | 9576 | 00:00:00.01 | 36 |
|* 4 | INDEX UNIQUE SCAN | ORD_ITEM_PK | 9576 | 1 | 261 | 00:00:00.17 | 28728 |
--------------------------------------------------------------------------------------------------------