Skip to content
튜닝 연습조인 튜닝
JOIN-028

시간대별 주문 집계에 USE_HASH를 줬는데 계획에는 MERGE JOIN이 나오고 TEMP를 쓴다

중급SELECT버거178 / 700

상황 ​

본사 영업기획팀의 「매장 · 시간대별 주문 수」는 2013년 주문을 매장과 시간대(아침, 점심, 오후, 저녁, 야간, 심야)로 나눠 센다. 시간대 구간은 마케팅팀이 관리하는 구간표 TM_BAND에 있다. 개발자는 큰 테이블 조인이니 해시 조인이 맞다며 USE_HASH를 넣었다. 그런데 실행계획에는 MERGE JOIN이 나오고, 돌 때마다 TEMP를 쓰며 3초가 걸린다. 구간표는 지금까지 늘 정시에서 바뀌었다.

테이블과 인덱스 ​

  • ORD 주문: 1,000,000행 · 22,422블록 · ORD_HMS VARCHAR2(6) 'HHMISS' (0을 채운 6자리) · 2013년 500,218행 · SHOP_NO 200곳
  • TM_BAND 시간대 구간표 (이 문제에서 쓰는 파생 테이블): 6행 · 4블록 · BAND_CD · BAND_NM · STA_HMS · END_HMS (예: 점심 '110000' ~ '135959') · 구간은 겹치지 않고 하루 24시간을 빈틈없이 덮는다 · 모든 경계가 정시다
  • 통계 최신 · pga_aggregate_target 512M · AUTO

원본 SQL ​

sql
SELECT /*+ USE_HASH(o b) */
       o.SHOP_NO, b.BAND_NM, COUNT(*) AS ord_cnt
  FROM ORD o, TM_BAND b
 WHERE o.ORD_DT BETWEEN '20130101' AND '20131231'
   AND o.ORD_HMS BETWEEN b.STA_HMS AND b.END_HMS
 GROUP BY o.SHOP_NO, b.BAND_NM

실행계획 학습용 예시 ​

xplan
----------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation             | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads | Writes |  OMem |  1Mem | Used-Mem | Used-Tmp|
----------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |         |      1 |        |   1200 |00:00:03.12 |   22426 |  2187 |   2187 |       |       |          |         |
|   1 |  HASH GROUP BY        |         |      1 |   1200 |   1200 |00:00:03.12 |   22426 |  2187 |   2187 |  1345K|  1345K| 1402K (0)|         |
|   2 |   MERGE JOIN          |         |      1 |   7503 |    500K|00:00:02.96 |   22426 |  2187 |   2187 |       |       |          |         |
|   3 |    SORT JOIN          |         |      1 |      6 |      6 |00:00:00.01 |       4 |     0 |      0 |  2048 |  2048 | 2048  (0)|         |
|   4 |     TABLE ACCESS FULL | TM_BAND |      1 |      6 |      6 |00:00:00.01 |       4 |     0 |      0 |       |       |          |         |
|*  5 |    FILTER             |         |      6 |        |    500K|00:00:02.88 |   22422 |  2187 |   2187 |       |       |          |         |
|*  6 |     SORT JOIN         |         |      6 |    500K|   1708K|00:00:02.61 |   22422 |  2187 |   2187 |    15M|  1489K|   13M (1)|      17M|
|*  7 |      TABLE ACCESS FULL| ORD     |      1 |    500K|    500K|00:00:00.31 |   22422 |     0 |      0 |       |       |          |         |
----------------------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   5 - filter("O"."ORD_HMS"<="B"."END_HMS")
   6 - access(INTERNAL_FUNCTION("O"."ORD_HMS")>=INTERNAL_FUNCTION("B"."STA_HMS"))
       filter(INTERNAL_FUNCTION("O"."ORD_HMS")>=INTERNAL_FUNCTION("B"."STA_HMS"))
   7 - filter(("O"."ORD_DT">='20130101' AND "O"."ORD_DT"<='20131231'))

문제 ​

  1. USE_HASH 힌트가 왜 무시됐는지 조인 조건으로 설명하고, 6번 줄 SORT JOIN이 무엇을 정렬하다 TEMP를 썼는지 설명하시오.
  2. 구간표를 기준으로 그대로 쓰면서 큰 테이블 쪽 조인이 등치 조건이 되도록 SQL을 고치시오. 고친 SQL이 맞으려면 구간표에 어떤 전제가 필요한지 쓰시오.
  3. 시간대를 CASE 식으로 SQL에 박아 넣는 안, ORD에 시간대 컬럼을 더하는 안과 비교해 대가를 서술하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자