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

「주말 · 지역 · 메뉴 분류 판매」 리포트가 고른 조건과 상관없이 늘 6만 블록을 읽는다

중급SELECT버거217 / 700

상황 ​

본사 BI팀은 작년에 정보계 구축 업체가 만든 판매 마트에서 리포트를 뽑는다. 「주말 · 지역 · 메뉴 분류 판매」는 연도, 지역, 주말 여부, 메뉴 분류를 골라 매장과 메뉴별 판매를 보는 리포트로, 지역 영업팀이 하루 150번쯤 돌린다. 구축 업체는 팩트 테이블의 조인 컬럼마다 비트맵 인덱스를 만들어 두고 "조건을 좁히면 인덱스로 빨라진다"고 인수인계했다. 그런데 조건을 아무리 좁혀도 팩트 테이블을 통째로 읽는다. 아래는 2013년 주말의 부산 지역 음료 판매를 조회한 실행계획이다.

테이블과 인덱스 ​

  • DW_ORD_ITEM 판매 팩트: 2,472,717행 · 60,940블록 · ORD_DT · SHOP_NO · ITEM_ID · ORD_ITEM_QTY · SLE_AMT · 매일 밤 전날 판매를 날짜순으로 덧붙인다 · 2013년 주말 383,165줄, 그중 부산 매장 22,990줄, 그중 음료 5,058줄
  • 비트맵 인덱스 BX_DOI_DT (ORD_DT) · BX_DOI_SHOP (SHOP_NO) · BX_DOI_ITEM (ITEM_ID)(각각 약 900 · 300 · 450블록) · 다른 인덱스 없음
  • DIM_DAY 날짜: 731행 · 5블록 · DT · YYYY · WKND_YN / DIM_SHOP 매장: 200행 · 5블록 · SHOP_NO · SHOP_NM · AREA_NM(부산 12곳) / ITEM 상품: 92행 · 5블록 · ITEM_TYPE_CD(음료 '100105' 14종)
  • 차원 조건 하나씩만 보면 2013년 주말은 팩트의 15.5%, 부산은 6%, 음료는 22%다 · STAR_TRANSFORMATION_ENABLED = FALSE(기본값) · 통계 최신

원본 SQL ​

sql
SELECT s.SHOP_NM, t.ITEM_NM, SUM(f.ORD_ITEM_QTY) AS qty, SUM(f.SLE_AMT) AS amt
  FROM DW_ORD_ITEM f, DIM_DAY d, DIM_SHOP s, ITEM t
 WHERE f.ORD_DT  = d.DT
   AND f.SHOP_NO = s.SHOP_NO
   AND f.ITEM_ID = t.ITEM_ID
   AND d.YYYY    = :v_yyyy
   AND d.WKND_YN = 'Y'
   AND s.AREA_NM = :v_area
   AND t.ITEM_TYPE_CD = :v_type
 GROUP BY s.SHOP_NM, t.ITEM_NM

실행계획 학습용 예시 ​

xplan
-------------------------------------------------------------------------------------------------------------
| Id  | Operation               | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  Used-Mem |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |             |      1 |        |    168 |00:00:02.41 |   60955 |           |
|   1 |  HASH GROUP BY          |             |      1 |    168 |    168 |00:00:02.41 |   60955 | 1186K (0) |
|*  2 |   HASH JOIN             |             |      1 |   3212 |   5058 |00:00:02.40 |   60955 | 1290K (0) |
|*  3 |    TABLE ACCESS FULL    | DIM_SHOP    |      1 |     12 |     12 |00:00:00.01 |       5 |           |
|*  4 |    HASH JOIN            |             |      1 |  53535 |  84296 |00:00:02.36 |   60950 | 1236K (0) |
|*  5 |     TABLE ACCESS FULL   | ITEM        |      1 |     14 |     14 |00:00:00.01 |       5 |           |
|*  6 |     HASH JOIN           |             |      1 |   352K |   383K |00:00:02.12 |   60945 | 1347K (0) |
|*  7 |      TABLE ACCESS FULL  | DIM_DAY     |      1 |    104 |    104 |00:00:00.01 |       5 |           |
|   8 |      TABLE ACCESS FULL  | DW_ORD_ITEM |      1 |  2472K |  2472K |00:00:00.31 |   60940 |           |
-------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("F"."SHOP_NO"="S"."SHOP_NO")
   3 - filter("S"."AREA_NM"=:V_AREA)
   4 - access("F"."ITEM_ID"="T"."ITEM_ID")
   5 - filter("T"."ITEM_TYPE_CD"=:V_TYPE)
   6 - access("F"."ORD_DT"="D"."DT")
   7 - filter(("D"."YYYY"=:V_YYYY AND "D"."WKND_YN"='Y'))

문제 ​

  1. 비트맵 인덱스가 셋이나 있는데 옵티마이저가 팩트 테이블을 FULL로 읽은 이유를 차원 조건 하나하나의 선택도로 설명하시오. 세 조건을 모두 적용하면 팩트에서 몇 행이 남는지도 쓰시오.
  2. 세 비트맵 인덱스를 함께 쓰는 계획이 나오도록 고치고, 그 계획이 어떻게 동작하는지 서술하시오. 이 방법이 맞지 않는 경우와 대가도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자