테마
공통 모듈의 「주문 상세」 SQL(PKG_ORD_COM.Q7)은 기간을 받아 주문과 주문상세를 주문번호 순으로 낸다. 상담 화면 「하루 주문 상세」가 하루치로 하루 3,000번 부르고, 회계팀의 월 배치 「연간 주문 상세 파일」이 한 달에 한 번 1년 치로 부른다. 두 곳은 SQL을 각자 복사해 써서 커서가 따로 생기고, 지금은 둘 다 힌트가 없다.
지난달 통계를 다시 모은 뒤 화면 쪽 계획이 해시로 바뀌어 화면이 느려졌다는 민원이 왔고, 작년에는 배치 쪽이 NL로 돌아 새벽 배치가 밀린 적이 있다. 조민우가 같은 SQL에 조인 방식 힌트만 바꿔 가며 두 기간을 재었다. 박팀장은 "세 방식을 비교한 의견서를 쓰고, 두 곳에 무엇을 쓸지 정해 달라"라고 했다.
T_ORD 주문: 100,000행 · 896블록 · PK_T_ORD (ORD_SEQ) · IX_T_ORD_DT (ORD_DT) · ORD_SEQ 순으로 쌓였고 같은 날짜의 주문은 테이블 곳곳에 흩어져 있다(하루 137건이 137블록) · 2024년 50,000건(테이블의 절반)T_ORD_DET 주문상세: 200,000행 · 896블록 · PK_T_ORD_DET (ORD_SEQ, ORD_DET_NO) (BLEVEL 1) · 주문당 1 ~ 3건(평균 2) · 주문 순서로 적재pga_aggregate_target 512M · AUTO · 월 배치가 도는 새벽에는 다른 배치 다섯 개가 함께 돈다LEADING(o d)로 순서를 고정하고 조인 방식 힌트만 바꿨다SELECT o.ORD_SEQ, o.ORD_DT, o.CUS_ID, d.ORD_DET_NO, d.ITM_ID, d.ORD_QTY, d.UNT_PRC
FROM T_ORD o, T_ORD_DET d
WHERE o.ORD_DT >= :d1
AND o.ORD_DT < :d2
AND d.ORD_SEQ = o.ORD_SEQ
ORDER BY o.ORD_SEQ, d.ORD_DET_NO
[측정 요약 · LEADING(o d) 고정, 조인 방식 힌트만 바꿈]
기간 방식 Buffers A-Time 조인 줄의 메모리 마지막 정렬
A 하루 (주문 137) NL USE_NL(d) 688 0.01초 없음 275줄 · 2048 (0)
해시 USE_HASH(d) 1,035 0.02초 HASH JOIN build 주문 137행 · 1,190K (0) 275줄 · 2048 (0)
머지 USE_MERGE(d) 1,035 0.14초 SORT JOIN 주문상세 20만 줄 · 8,960K (0) 275줄 · 2048 (0)
B 1년 (주문 50,000) NL USE_NL(d) 201K 0.71초 없음 10만 줄 · 6,420K (0)
해시 USE_HASH(d) 1,792 0.26초 HASH JOIN build 주문 5만 행 · 3,480K (0) 10만 줄 · 6,420K (0)
머지 USE_MERGE(d) 1,792 0.44초 SORT JOIN 주문 5만 행 · 2,310K (0) 10만 줄 · 6,420K (0)
+ SORT JOIN 주문상세 20만 줄 · 8,960K (0)
(A는 T_ORD를 IX_T_ORD_DT로, B는 T_ORD 절반을 읽으므로 세 방식 모두 FULL로 읽었다)
-------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Used-Mem |
-------------------------------------------------------------------------------------------------------------------------
[B 1년 · NL]
| 0 | SELECT STATEMENT | | 1 | | 100K |00:00:00.71 | 201K | |
| 1 | SORT ORDER BY | | 1 | 100K | 100K |00:00:00.71 | 201K | 6420K (0)|
| 2 | NESTED LOOPS | | 1 | 100K | 100K |00:00:00.58 | 201K | |
| 3 | NESTED LOOPS | | 1 | 100K | 100K |00:00:00.29 | 101K | |
|* 4 | TABLE ACCESS FULL | T_ORD | 1 | 50000 | 50000 |00:00:00.02 | 896 | |
|* 5 | INDEX RANGE SCAN | PK_T_ORD_DET | 50000 | 2 | 100K |00:00:00.21 | 100K | |
| 6 | TABLE ACCESS BY INDEX ROWID | T_ORD_DET | 100K | 2 | 100K |00:00:00.24 | 100K | |
[B 1년 · 소트 머지]
| 0 | SELECT STATEMENT | | 1 | | 100K |00:00:00.44 | 1792 | |
| 1 | SORT ORDER BY | | 1 | 100K | 100K |00:00:00.44 | 1792 | 6420K (0)|
| 2 | MERGE JOIN | | 1 | 100K | 100K |00:00:00.36 | 1792 | |
| 3 | SORT JOIN | | 1 | 50000 | 50000 |00:00:00.05 | 896 | 2310K (0)|
|* 4 | TABLE ACCESS FULL | T_ORD | 1 | 50000 | 50000 |00:00:00.02 | 896 | |
|* 5 | SORT JOIN | | 50000 | 200K | 100K |00:00:00.27 | 896 | 8960K (0)|
| 6 | TABLE ACCESS FULL | T_ORD_DET | 1 | 200K | 200K |00:00:00.03 | 896 | |
-------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
[NL] 4 - filter(("O"."ORD_DT">=:D1 AND "O"."ORD_DT"<:D2))
5 - access("D"."ORD_SEQ"="O"."ORD_SEQ")
[머지] 4 - filter(("O"."ORD_DT">=:D1 AND "O"."ORD_DT"<:D2))
5 - access("D"."ORD_SEQ"="O"."ORD_SEQ")
filter("D"."ORD_SEQ"="O"."ORD_SEQ")
Starts, Used-Mem으로 비교해 서술하시오. 소트 머지 계획 5번 줄의 Starts 50,000이 무엇을 뜻하는지도 쓰시오.