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

「월간 주문 채널 분석」 추출에 칸을 하나 더할 때마다 읽는 블록이 2천 개씩 늘어난다

중급SELECT한빛유통216 / 700

상황 ​

마케팅팀의 「월간 주문 채널 분석」은 한 달 주문마다 유입 채널, 쿠폰 번호, 선물 포장 여부, 할부 개월을 붙여 내려받는 추출이다. 처음에는 유입 채널 한 칸이었는데 요청이 올 때마다 칸을 하나씩 더해 지금은 네 칸이 됐고, 칸이 늘 때마다 읽는 블록도 2천여 개씩 늘었다. 마케팅팀은 다음 달에 배송 메모와 영수증 종류도 더해 달라고 한다. 부가 속성은 2021년 주문 화면 개편 때 T_ORD에 컬럼을 늘리지 않고 행으로 넣기로 한 T_ORD_ATTR에 있다. 아래는 2024년 6월로 조회한 실행계획이다.

테이블과 인덱스 ​

  • T_ORD: 100,000행 · 896블록 · PK_T_ORD (ORD_SEQ) · 2024년 6월 4,172건(테이블 전체에 흩어져 있다)
  • T_ORD_ATTR 주문 부가 속성(이 문제에서 쓰는 테이블): 400,000행 · 2,220블록 · ORD_SEQ · ATTR_CD · ATTR_VAL · PK_T_ORD_ATTR (ORD_SEQ, ATTR_CD) BLEVEL 2
  • ATTR_CD 6종의 행 수: CHNL 98,800 · RCPT_TP 120,000 · DLV_MSG 81,200 · PAY_INST 60,000 · CPN 30,000 · GIFT_YN 10,000 · ATTR_CD에 히스토그램 있음
  • 6월 주문의 속성 행: CHNL 4,135 · PAY_INST 2,497 · CPN 1,254 · GIFT_YN 418 · 네 속성이 모두 없는 주문 37건(콜센터 전화 주문) · 통계 최신

원본 SQL ​

sql
SELECT o.ORD_SEQ, o.ORD_DT, o.ORD_AMT,
       a1.ATTR_VAL AS chnl, a2.ATTR_VAL AS cpn_no,
       a3.ATTR_VAL AS gift_yn, a4.ATTR_VAL AS pay_inst
  FROM T_ORD o
  LEFT JOIN T_ORD_ATTR a1 ON a1.ORD_SEQ = o.ORD_SEQ AND a1.ATTR_CD = 'CHNL'
  LEFT JOIN T_ORD_ATTR a2 ON a2.ORD_SEQ = o.ORD_SEQ AND a2.ATTR_CD = 'CPN'
  LEFT JOIN T_ORD_ATTR a3 ON a3.ORD_SEQ = o.ORD_SEQ AND a3.ATTR_CD = 'GIFT_YN'
  LEFT JOIN T_ORD_ATTR a4 ON a4.ORD_SEQ = o.ORD_SEQ AND a4.ATTR_CD = 'PAY_INST'
 WHERE o.ORD_DT >= :v_from
   AND o.ORD_DT <  :v_to

실행계획 학습용 예시 ​

xplan
------------------------------------------------------------------------------------------------------------
| Id  | Operation               | Name       | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  Used-Mem |
------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |            |      1 |        |   4172 |00:00:00.21 |    9776 |           |
|*  1 |  HASH JOIN OUTER        |            |      1 |   4172 |   4172 |00:00:00.21 |    9776 | 1586K (0) |
|*  2 |   HASH JOIN OUTER       |            |      1 |   4172 |   4172 |00:00:00.16 |    7556 | 1586K (0) |
|*  3 |    HASH JOIN OUTER      |            |      1 |   4172 |   4172 |00:00:00.11 |    5336 | 1586K (0) |
|*  4 |     HASH JOIN OUTER     |            |      1 |   4172 |   4172 |00:00:00.06 |    3116 | 1452K (0) |
|*  5 |      TABLE ACCESS FULL  | T_ORD      |      1 |   4172 |   4172 |00:00:00.01 |     896 |           |
|*  6 |      TABLE ACCESS FULL  | T_ORD_ATTR |      1 |  98800 |  98800 |00:00:00.03 |    2220 |           |
|*  7 |     TABLE ACCESS FULL   | T_ORD_ATTR |      1 |  30000 |  30000 |00:00:00.03 |    2220 |           |
|*  8 |    TABLE ACCESS FULL    | T_ORD_ATTR |      1 |  10000 |  10000 |00:00:00.03 |    2220 |           |
|*  9 |   TABLE ACCESS FULL     | T_ORD_ATTR |      1 |  60000 |  60000 |00:00:00.03 |    2220 |           |
------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("A4"."ORD_SEQ"="O"."ORD_SEQ")
   2 - access("A3"."ORD_SEQ"="O"."ORD_SEQ")
   3 - access("A2"."ORD_SEQ"="O"."ORD_SEQ")
   4 - access("A1"."ORD_SEQ"="O"."ORD_SEQ")
   5 - filter(("O"."ORD_DT">=:V_FROM AND "O"."ORD_DT"<:V_TO))
   6 - filter("A1"."ATTR_CD"='CHNL')
   7 - filter("A2"."ATTR_CD"='CPN')
   8 - filter("A3"."ATTR_CD"='GIFT_YN')
   9 - filter("A4"."ATTR_CD"='PAY_INST')

문제 ​

  1. 칸을 하나 더할 때마다 읽는 블록이 왜 2천 개 남짓씩 느는지 실행계획의 줄 번호로 설명하시오.
  2. T_ORD_ATTR를 한 번만 읽도록 SQL을 고치시오. 결과 행 수와 각 칸의 값이 원래 SQL과 같아야 하며, 그렇게 되는 근거를 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자