테마
「월 청구서 추출」은 매달 1일 새벽, 한 달 치 주문을 고객 순으로 뽑아 청구서 출력 프로그램에 넘긴다. 프로그램은 고객 번호가 바뀔 때마다 새 장을 시작하므로 고객 순서가 꼭 맞아야 한다.
지난달 조민우가 계획의 SORT JOIN 줄 Starts가 2,000인 것을 보고 "고객마다 주문을 다시 정렬하느라 2,000번 돈다"라며 힌트를 USE_MERGE에서 USE_NL로 바꿨다. 이번 달 추출은 읽는 블록이 932에서 8,249로 늘었다. 아래는 2024년 6월로 돌린 지금의 계획과 바꾸기 전의 계획이다.
M_CUS: 2,000행 · 30블록 · PK_M_CUS (CUS_ID) · 고객 번호 순으로 적재되어 있다T_ORD: 100,000행 · 896블록 · PK_T_ORD (ORD_SEQ) · IX_T_ORD_CUS_DT (CUS_ID, ORD_DT) · 한 고객의 주문은 테이블 여기저기에 흩어져 있다SELECT /*+ LEADING(c o) USE_NL(o) INDEX(o IX_T_ORD_CUS_DT) */
c.CUS_ID, c.CUS_NM, o.ORD_SEQ, o.ORD_DT, o.ORD_AMT
FROM M_CUS c, T_ORD o
WHERE o.CUS_ID = c.CUS_ID
AND o.ORD_DT >= :v_from
AND o.ORD_DT < :v_to
ORDER BY c.CUS_ID
-- 바꾸기 전 힌트 : /*+ LEADING(c o) USE_MERGE(o) */
[지금 · USE_NL]
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 4172 |00:00:00.11 | 8249 |
| 1 | NESTED LOOPS | | 1 | 4170 | 4172 |00:00:00.11 | 8249 |
| 2 | NESTED LOOPS | | 1 | 4170 | 4172 |00:00:00.04 | 4077 |
| 3 | TABLE ACCESS BY INDEX ROWID| M_CUS | 1 | 2000 | 2000 |00:00:00.01 | 36 |
| 4 | INDEX FULL SCAN | PK_M_CUS | 1 | 2000 | 2000 |00:00:00.01 | 6 |
|* 5 | INDEX RANGE SCAN | IX_T_ORD_CUS_DT | 2000 | 2 | 4172 |00:00:00.02 | 4041 |
| 6 | TABLE ACCESS BY INDEX ROWID | T_ORD | 4172 | 2 | 4172 |00:00:00.06 | 4172 |
-----------------------------------------------------------------------------------------------------------
5 - access("O"."CUS_ID"="C"."CUS_ID" AND "O"."ORD_DT">=:V_FROM AND "O"."ORD_DT"<:V_TO)
[바꾸기 전 · USE_MERGE]
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Used-Mem |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 4172 |00:00:00.02 | 932 | |
| 1 | MERGE JOIN | | 1 | 4170 | 4172 |00:00:00.02 | 932 | |
| 2 | TABLE ACCESS BY INDEX ROWID | M_CUS | 1 | 2000 | 2000 |00:00:00.01 | 36 | |
| 3 | INDEX FULL SCAN | PK_M_CUS | 1 | 2000 | 2000 |00:00:00.01 | 6 | |
|* 4 | SORT JOIN | | 2000 | 4170 | 4172 |00:00:00.01 | 896 | 267K (0)|
|* 5 | TABLE ACCESS FULL | T_ORD | 1 | 4170 | 4172 |00:00:00.01 | 896 | |
----------------------------------------------------------------------------------------------------------
4 - access("O"."CUS_ID"="C"."CUS_ID")
filter("O"."CUS_ID"="C"."CUS_ID")
5 - filter(("O"."ORD_DT">=:V_FROM AND "O"."ORD_DT"<:V_TO))
SORT JOIN Starts 2,000이 무엇을 센 것인지 설명하고, 조민우의 해석이 왜 틀렸는지 쓰시오.