테마
본사 영업팀의 「행사 매출」 화면은 행사 상품 두 개 중 하나라도 든 영수증의 매장별 매출을 기간으로 보여 준다. 매장 담당자는 하루 단위로(하루 1,500번), 영업팀장은 한 달 단위로(하루 30번) 본다.
지난달 이 SQL이 월 조회로 먼저 파싱된 날, 일 조회까지 해시 세미 조인 계획을 쓰면서 느려졌다. 개발자가 서브쿼리에 NO_UNNEST를 붙이자 일 조회는 나아졌는데, 이번에는 월 조회가 0.4초를 넘긴다.
CF10 판매: 500,000행 · 4,583블록 · CF10_PK (SLE_NO) · IX_CF10_SLE_DT (SLE_DT) (하루치가 약 14블록에 모여 있다) · 하루 영수증 706장, 한 달 21,180장CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) (높이 3, 영수증 한 장의 줄은 한 블록에 모여 있다) · 영수증당 평균 2.2줄:v_i1, :v_i2)의 판매 줄은 전 기간 25,222줄 · 행사 상품이 든 영수증은 전체의 약 5%(하루 35장, 한 달 1,060장)SELECT a.SHP_NO, SUM(b.SLE_QTY * b.SLE_UPRC) AS amt, COUNT(*) AS line_cnt
FROM CF10 a, CF11 b
WHERE a.SLE_DT BETWEEN :v_from AND :v_to
AND b.SLE_NO = a.SLE_NO
AND EXISTS (SELECT /*+ NO_UNNEST */ 1
FROM CF11 x
WHERE x.SLE_NO = a.SLE_NO
AND x.ITM_NO IN (:v_i1, :v_i2))
GROUP BY a.SHP_NO
-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 5 |00:00:00.43 | 148764 |
| 1 | HASH GROUP BY | | 1 | 5 | 5 |00:00:00.43 | 148764 |
|* 2 | FILTER | | 1 | | 2322 |00:00:00.43 | 148764 |
| 3 | NESTED LOOPS | | 1 | 46400 | 46384 |00:00:00.25 | 85224 |
| 4 | NESTED LOOPS | | 1 | 46400 | 46384 |00:00:00.17 | 64044 |
| 5 | TABLE ACCESS BY INDEX ROWID BATCHED | CF10 | 1 | 21180 | 21180 |00:00:00.01 | 504 |
|* 6 | INDEX RANGE SCAN | IX_CF10_SLE_DT | 1 | 21180 | 21180 |00:00:00.01 | 84 |
|* 7 | INDEX RANGE SCAN | CF11_PK | 21180 | 2 | 46384 |00:00:00.12 | 63540 |
| 8 | TABLE ACCESS BY INDEX ROWID | CF11 | 46384 | 2 | 46384 |00:00:00.06 | 21180 |
|* 9 | INDEX RANGE SCAN | CF11_PK | 21180 | 1 | 1060 |00:00:00.13 | 63540 |
-------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "CF11" "X" WHERE "X"."SLE_NO"=:B1 AND
("X"."ITM_NO"=:V_I1 OR "X"."ITM_NO"=:V_I2)))
6 - access("A"."SLE_DT">=:V_FROM AND "A"."SLE_DT"<=:V_TO)
7 - access("B"."SLE_NO"="A"."SLE_NO")
9 - access("X"."SLE_NO"=:B1)
filter(("X"."ITM_NO"=:V_I1 OR "X"."ITM_NO"=:V_I2))
PUSH_SUBQ로 먼저 거르기, 펼쳐서 NL 세미 조인, 펼쳐서 해시 세미 조인)의 일량을 어림해 비교하시오.