테마
버거 본사 마케팅팀이 시간대 분석 화면 세 개를 사내에서 새로 만들었다. 점심 피크 메뉴(R1), 12시대 매장별 주문 수(R2), 저녁 피크 메뉴(R3)다. 개발자는 "시각 문자열의 앞 두 자리만 잘라 비교하면 간단하다"고 했고, R3은 다른 개발자가 숫자로 바꿔 비교하게 썼다. R1과 R3은 3초 가까이 걸리고, R2는 0.2초다. 다음 주 메뉴 개편 회의 전까지 하루 수십 번씩 돌린다. 선배는 셋을 같이 보고 한 번에 고칠 길을 고르라고 했다. 세 SQL은 고칠 수 있다.
ORD 주문: 1,000,000행 · 22,422블록 · ORD_HMS VARCHAR2(6) 'HH24MISS'(늘 6자리, 영업시간 09:00:00 ~ 22:59:59) · 시간대마다 고르게 쌓였다(한 시간에 약 71,429건)ORD_ITEM 주문상품: 2,472,717행 · 55,265블록 · ORD_ITEM_PK (ORD_NO, ITEM_ID): BLEVEL 2 · 주문당 평균 2.47줄 · 주문번호 순으로 쌓였다 · 주문의 ORD_DT와 ORD_HMS를 그대로 복사해 둔 열이 있다ITEM 상품: 92행 · 5블록-- R1 점심 피크 메뉴 (11시 ~ 13시대)
SELECT i.ITEM_NM, SUM(oi.ORD_ITEM_QTY) AS qty
FROM ORD o, ORD_ITEM oi, ITEM i
WHERE SUBSTR(o.ORD_HMS, 1, 2) BETWEEN '11' AND '13'
AND oi.ORD_NO = o.ORD_NO
AND i.ITEM_ID = oi.ITEM_ID
GROUP BY i.ITEM_NM;
-- R2 12시대 매장별 주문 수
SELECT o.SHOP_NO, COUNT(*) AS ord_cnt
FROM ORD o
WHERE SUBSTR(o.ORD_HMS, 1, 2) = '12'
GROUP BY o.SHOP_NO;
-- R3 저녁 피크 메뉴 (18시 ~ 20시대)
SELECT i.ITEM_NM, SUM(oi.ORD_ITEM_QTY) AS qty
FROM ORD o, ORD_ITEM oi, ITEM i
WHERE TO_NUMBER(o.ORD_HMS) BETWEEN 180000 AND 205959
AND oi.ORD_NO = o.ORD_NO
AND i.ITEM_ID = oi.ITEM_ID
GROUP BY i.ITEM_NM;
[R1]
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 92 |00:00:02.91 | 667K |
| 1 | HASH GROUP BY | | 1 | 92 | 92 |00:00:02.91 | 667K |
|* 2 | HASH JOIN | | 1 | 6175 | 529K|00:00:02.85 | 667K |
| 3 | TABLE ACCESS FULL | ITEM | 1 | 92 | 92 |00:00:00.01 | 5 |
| 4 | NESTED LOOPS | | 1 | 6175 | 529K|00:00:02.71 | 667K |
| 5 | NESTED LOOPS | | 1 | 6175 | 529K|00:00:01.32 | 452K |
|* 6 | TABLE ACCESS FULL | ORD | 1 | 2500 | 214K|00:00:00.21 | 22422 |
|* 7 | INDEX RANGE SCAN | ORD_ITEM_PK | 214K| 2 | 529K|00:00:00.98 | 430K |
| 8 | TABLE ACCESS BY INDEX ROWID| ORD_ITEM | 529K| 1 | 529K|00:00:01.21 | 215K |
----------------------------------------------------------------------------------------------------
2 - access("I"."ITEM_ID"="OI"."ITEM_ID")
6 - filter((SUBSTR("O"."ORD_HMS",1,2)>='11' AND SUBSTR("O"."ORD_HMS",1,2)<='13'))
7 - access("OI"."ORD_NO"="O"."ORD_NO")
[R2] TABLE ACCESS FULL ORD · filter(SUBSTR("O"."ORD_HMS",1,2)='12') · E-Rows 10,000 / A-Rows 71,429
→ HASH GROUP BY · Buffers 22,422 · 0.2초
[R3] R1과 같은 모양(ORD FULL → NL ORD_ITEM_PK) · filter(TO_NUMBER("O"."ORD_HMS")>=180000 AND
TO_NUMBER("O"."ORD_HMS")<=205959) · E-Rows 2,500 / A-Rows 214,286 · Buffers 667K · 2.9초
E-Rows(2,500 · 10,000 · 2,500)가 어디서 나왔는지 쓰고, 셋 다 틀렸는데 R2만 느리지 않은 이유를 설명하시오.