Skip to content
튜닝 연습서브쿼리와 쿼리 변환
SUBQ-061

구매팀 「상품별 발주 대비 판매」 화면의 판매 수량이 POS 일보의 2%도 안 된다

중급SELECT유통296 / 700

상황 ​

구매팀이 새로 만든 「상품별 발주 대비 판매」 화면을 시험하던 오과장이 이렇게 알려 왔다. "6월 판매 수량 합계가 POS 일보는 61,214개인데 이 화면은 1,138개다. 발주도 원장은 24,992개인데 21,872개로 나온다." 개발자는 발주와 판매를 한 목록으로 이어 붙인 뒤 상품별로 더했다. 두 목록에 겹치는 행은 없으니 UNION이나 UNION ALL이나 같다고 생각했다고 한다.

테이블과 인덱스 ​

  • CF12 발주: 734행 · 10블록 · ORD_DT VARCHAR2(8) · 2023년 6월 28건 / CF13 발주상품: 7,102행 · 57블록 · 6월 271줄(상품 68개)
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) · 판매번호는 판매일자 8자리로 시작한다 · 6월 45,935줄(상품 88개) · 반품은 수량이 음수로 들어간다
  • CF01 상품: 109행 · 5블록 · 통계 최신
  • 화면은 :v_from = '20230601', :v_to = '20230630', :v_next = '20230701'을 넘긴다

원본 SQL ​

sql
SELECT x.ITM_NO, i.ITM_NM, SUM(x.ORD_QTY) AS ORD_QTY, SUM(x.SLE_QTY) AS SLE_QTY
  FROM (SELECT d.ITM_NO, d.ORD_QTY, 0 AS SLE_QTY
          FROM CF12 h, CF13 d
         WHERE h.ORD_DT BETWEEN :v_from AND :v_to
           AND d.ORD_NO = h.ORD_NO
        UNION
        SELECT b.ITM_NO, 0, b.SLE_QTY
          FROM CF11 b
         WHERE b.SLE_NO >= :v_from
           AND b.SLE_NO <  :v_next) x,
       CF01 i
 WHERE i.ITM_NO = x.ITM_NO
 GROUP BY x.ITM_NO, i.ITM_NM
 ORDER BY x.ITM_NO

실행계획 학습용 예시 ​

xplan
-----------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                  | Name    | Starts | E-Rows | A-Rows |      A-Time | Buffers |  Used-Mem |
-----------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                           |         |      1 |        |     97 | 00:00:00.09 |    1396 |           |
|   1 |  SORT GROUP BY                             |         |      1 |    109 |     97 | 00:00:00.09 |    1396 |  9216 (0) |
|*  2 |   HASH JOIN                                |         |      1 |  46206 |    638 | 00:00:00.09 |    1396 | 1236K (0) |
|   3 |    TABLE ACCESS FULL                       | CF01    |      1 |    109 |    109 | 00:00:00.01 |       5 |           |
|   4 |    VIEW                                    |         |      1 |  46206 |    638 | 00:00:00.09 |    1391 |           |
|   5 |     SORT UNIQUE                            |         |      1 |  46206 |    638 | 00:00:00.09 |    1391 | 2048K (0) |
|   6 |      UNION-ALL                             |         |      1 |        |  46206 | 00:00:00.03 |    1391 |           |
|*  7 |       HASH JOIN                            |         |      1 |    262 |    271 | 00:00:00.01 |      67 | 1321K (0) |
|*  8 |        TABLE ACCESS FULL                   | CF12    |      1 |     27 |     28 | 00:00:00.01 |      10 |           |
|   9 |        TABLE ACCESS FULL                   | CF13    |      1 |   7102 |   7102 | 00:00:00.01 |      57 |           |
|  10 |       TABLE ACCESS BY INDEX ROWID BATCHED  | CF11    |      1 |  45935 |  45935 | 00:00:00.02 |    1324 |           |
|* 11 |        INDEX RANGE SCAN                    | CF11_PK |      1 |  45935 |  45935 | 00:00:00.01 |     176 |           |
-----------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("I"."ITM_NO"="X"."ITM_NO")
   7 - access("D"."ORD_NO"="H"."ORD_NO")
   8 - filter(("H"."ORD_DT">=:V_FROM AND "H"."ORD_DT"<=:V_TO))
  11 - access("B"."SLE_NO">=:V_FROM AND "B"."SLE_NO"<:V_NEXT)

문제 ​

  1. 판매 수량과 일부 발주 수량이 원장보다 적게 나오는 이유를 실행계획의 줄 번호와 A-Rows로 설명하시오.
  2. 원장과 맞도록 SQL을 고치시오. 발주만 있는 상품과 판매만 있는 상품이 모두 나와야 하는데, 고친 SQL이 그것을 어떻게 지키는지도 쓰시오.
  3. 두 집계를 FULL OUTER JOIN으로 붙이는 안과 비교하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자