테마
본사 품질팀은 매장에서 상품 클레임이 들어오면 「클레임 역추적」 화면에 날짜와 상품을 넣어, 그날 그 상품이 팔린 주문(매장, 시각)을 뽑는다. 클레임이 몰리는 날은 수십 번 돌리는데 한 번에 6초가 걸린다.
서브쿼리의 NO_UNNEST 옆에는 -- 서브쿼리 먼저 풀지 말 것 (2014.02 오과장)이라는 주석이 있다. 2014년에 이 SQL은 바깥에도 o.ORD_DT = :dt가 있었고, ORD_ITEM에는 PK 말고 인덱스가 없었다. 2019년 리팩터링에서 "날짜는 서브쿼리에 있으니 중복"이라며 바깥의 날짜 조건을 지웠다.
ORD 주문: 1,000,000행 · 22,422블록 · ORD_PK (ORD_NO) (높이 3) · ORD_X01 (ORD_DT, ORD_HMS) · 하루 주문 약 1,370건ORD_ITEM 주문상품: 2,472,717행 · 55,265블록 · ORD_ITEM_PK (ORD_NO, ITEM_ID) (높이 3) · ORD_ITEM_X01 (ORD_DT, ORD_HMS) (2016년 추가, 하루치가 약 76블록에 모여 있다) · ORD_DT는 주문 헤더의 날짜를 그대로 복사한 값이다SELECT o.ORD_NO, o.ORD_HMS, o.SHOP_NO
FROM ORD o
WHERE o.ORD_NO IN (SELECT /*+ NO_UNNEST */ i.ORD_NO -- 서브쿼리 먼저 풀지 말 것 (2014.02 오과장)
FROM ORD_ITEM i
WHERE i.ORD_DT = :dt
AND i.ITEM_ID = :item)
ORDER BY o.ORD_HMS
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 37 |00:00:06.12 | 3049299 |
| 1 | SORT ORDER BY | | 1 | 1 | 37 |00:00:06.12 | 3049299 |
|* 2 | FILTER | | 1 | | 37 |00:00:06.12 | 3049299 |
| 3 | TABLE ACCESS FULL | ORD | 1 | 1000K| 1000K|00:00:00.31 | 22422 |
|* 4 | TABLE ACCESS BY INDEX ROWID| ORD_ITEM | 1000K| 1 | 37 |00:00:05.21 | 3026877 |
|* 5 | INDEX UNIQUE SCAN | ORD_ITEM_PK | 1000K| 1 | 26877 |00:00:04.02 | 3000000 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "ORD_ITEM" "I" WHERE "I"."ORD_NO"=:B1
AND "I"."ITEM_ID"=:ITEM AND "I"."ORD_DT"=:DT))
4 - filter("I"."ORD_DT"=:DT)
5 - access("I"."ORD_NO"=:B1 AND "I"."ITEM_ID"=:ITEM)
HASH UNIQUE가 왜 붙는지, 이 SQL을 조인으로 직접 쓴다면 무엇을 조심해야 하는지 쓰시오.