테마
본사 메뉴팀의 「월간 메뉴 판매표」는 한 달 치 메뉴별 판매 수량과 판매 줄 수를 메뉴 유형별로 늘어놓는다. 지난달 메뉴팀이 "유형마다 소계 줄을, 맨 아래에는 전체 합계 줄을 붙여 달라"고 해서, 개발자가 같은 집계를 묶는 기준만 바꿔 세 번 쓰고 UNION ALL로 이었다.
월초 사흘 동안 메뉴팀과 지역 담당이 하루 300번쯤 여는데, 그 뒤로 화면이 0.1초에서 0.4초로 느려졌다. 아래는 2013년 12월로 연 실행계획이다.
ORD_ITEM 주문상품: 2,472,717행 · 55,265블록 · ORD_ITEM_X01 (ORD_DT, ORD_HMS) · 주문일 순으로 쌓여 있다. 2013년 12월은 104,867줄(테이블 2,345블록, 인덱스 리프 약 420블록)이다.ITEM 상품: 92행 · 5블록 · PK (ITEM_ID) · ITEM_TYPE_CD(메뉴 유형 5종)와 ITEM_NM은 NOT NULL이다. 12월에는 92개 메뉴가 모두 팔렸다.ORD_ITEM.ITEM_ID에는 ITEM을 가리키는 FK가 있다. 통계는 최신이다.SELECT ITEM_TYPE_CD, ITEM_ID, ITEM_NM, qty, lines
FROM (SELECT t.ITEM_TYPE_CD, t.ITEM_ID, t.ITEM_NM, 1 AS lvl,
SUM(i.ORD_ITEM_QTY) AS qty, COUNT(*) AS lines
FROM ORD_ITEM i, ITEM t
WHERE i.ORD_DT BETWEEN :d_from AND :d_to
AND t.ITEM_ID = i.ITEM_ID
GROUP BY t.ITEM_TYPE_CD, t.ITEM_ID, t.ITEM_NM
UNION ALL
SELECT t.ITEM_TYPE_CD, NULL, '유형 소계', 2,
SUM(i.ORD_ITEM_QTY), COUNT(*)
FROM ORD_ITEM i, ITEM t
WHERE i.ORD_DT BETWEEN :d_from AND :d_to
AND t.ITEM_ID = i.ITEM_ID
GROUP BY t.ITEM_TYPE_CD
UNION ALL
SELECT NULL, NULL, '전체 합계', 3,
SUM(i.ORD_ITEM_QTY), COUNT(*)
FROM ORD_ITEM i
WHERE i.ORD_DT BETWEEN :d_from AND :d_to)
ORDER BY ITEM_TYPE_CD NULLS LAST, lvl, qty DESC
[2013년 12월]
---------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Used-Mem |
---------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 98 |00:00:00.41 | 8320 | |
| 1 | SORT ORDER BY | | 1 | 98 | 98 |00:00:00.41 | 8320 | 2048 (0) |
| 2 | VIEW | | 1 | 98 | 98 |00:00:00.41 | 8320 | |
| 3 | UNION-ALL | | 1 | | 98 |00:00:00.41 | 8320 | |
| 4 | HASH GROUP BY | | 1 | 92 | 92 |00:00:00.15 | 2775 | 1124K (0)|
|* 5 | FILTER | | 1 | | 104K |00:00:00.11 | 2775 | |
|* 6 | HASH JOIN | | 1 | 104K | 104K |00:00:00.10 | 2775 | 1236K (0)|
| 7 | TABLE ACCESS FULL | ITEM | 1 | 92 | 92 |00:00:00.01 | 5 | |
| 8 | TABLE ACCESS BY INDEX ROWID BATCHED| ORD_ITEM | 1 | 104K | 104K |00:00:00.06 | 2770 | |
|* 9 | INDEX RANGE SCAN | ORD_ITEM_X01 | 1 | 104K | 104K |00:00:00.02 | 425 | |
| 10 | HASH GROUP BY | | 1 | 5 | 5 |00:00:00.14 | 2775 | 1018K (0)|
|* 11 | FILTER | | 1 | | 104K |00:00:00.10 | 2775 | |
|* 12 | HASH JOIN | | 1 | 104K | 104K |00:00:00.09 | 2775 | 1236K (0)|
| 13 | TABLE ACCESS FULL | ITEM | 1 | 92 | 92 |00:00:00.01 | 5 | |
| 14 | TABLE ACCESS BY INDEX ROWID BATCHED| ORD_ITEM | 1 | 104K | 104K |00:00:00.05 | 2770 | |
|* 15 | INDEX RANGE SCAN | ORD_ITEM_X01 | 1 | 104K | 104K |00:00:00.02 | 425 | |
| 16 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.12 | 2770 | |
|* 17 | FILTER | | 1 | | 104K |00:00:00.09 | 2770 | |
| 18 | TABLE ACCESS BY INDEX ROWID BATCHED | ORD_ITEM | 1 | 104K | 104K |00:00:00.05 | 2770 | |
|* 19 | INDEX RANGE SCAN | ORD_ITEM_X01 | 1 | 104K | 104K |00:00:00.02 | 425 | |
---------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - filter(:D_FROM<=:D_TO)
6 - access("T"."ITEM_ID"="I"."ITEM_ID")
9 - access("I"."ORD_DT">=:D_FROM AND "I"."ORD_DT"<=:D_TO)
11 - filter(:D_FROM<=:D_TO)
12 - access("T"."ITEM_ID"="I"."ITEM_ID")
15 - access("I"."ORD_DT">=:D_FROM AND "I"."ORD_DT"<=:D_TO)
17 - filter(:D_FROM<=:D_TO)
19 - access("I"."ORD_DT">=:D_FROM AND "I"."ORD_DT"<=:D_TO)