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

재고 테이블에 원재료가 들어오기 시작한 달부터 월말 수불 점검 배치가 ORA-01400으로 멈춘다

실전INSERT유통196 / 700

상황 ​

구매팀의 「월말 수불 점검」 배치는 품목마다 그달 판매 수량, 발주 수량, 월말 재고를 한 줄로 모아 CF_ITM_MON_CHK에 넣는다. 셋 중 어느 하나만 있는 품목도 빠지면 안 되어서 FULL OUTER JOIN 두 개로 이었다. 2023년 12월부터 매장 재고(CF18)에 원재료도 올리기 시작했는데, 그달 배치가 ORA-01400: cannot insert NULL into ("PUP"."CF_ITM_MON_CHK"."ITM_NO")로 멈췄다. 오과장은 SELECT만 따로 돌려 보니 129줄이 나왔다며 "세 집합을 합쳐도 품목은 109개인데 왜 129줄인지, 고치는 방법을 비교해서 가져와"라고 했다.

테이블과 인덱스 ​

  • CF01 상품: 109행 · 5블록 · 판매상품 88 · 추가상품 1(샷추가) · 원재료 20
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) · SLE_NO 앞 8자리가 판매일자 · 2023년 12월 46,322줄(판매상품 88개)
  • CF12 발주: 734행 · 10블록 · ORD_DT VARCHAR2(8) · 12월 62건 / CF13 발주상품: 7,102행 · 57블록 · 12월 604줄(원재료 20개, 판매상품 27개)
  • CF18 일별상품재고: 408행 · 6블록 · PK (SHP_NO, ITM_NO, INV_BS_DT) · 2023-12-31 기준 136행(68개 품목: 원재료 20, 판매상품 47, 마스터에서 지운 단종 품목 1)
  • CF_ITM_MON_CHK 점검 테이블: PK (CHK_YM, ITM_NO) · ITM_NO는 NOT NULL · 통계 최신
  • 바인드 :v_from = '20231201' · :v_to = '20240101' · :v_last = '20231231'

원본 SQL ​

sql
INSERT INTO CF_ITM_MON_CHK (CHK_YM, ITM_NO, SLE_QTY, ORD_QTY, INV_QTY)
SELECT :v_ym, NVL(s.ITM_NO, o.ITM_NO), s.qty, o.qty, k.qty
  FROM (SELECT ITM_NO, SUM(SLE_QTY) AS qty
          FROM CF11
         WHERE SLE_NO >= :v_from AND SLE_NO < :v_to
         GROUP BY ITM_NO) s
  FULL OUTER JOIN
       (SELECT d.ITM_NO, SUM(d.ORD_QTY) AS qty
          FROM CF12 h, CF13 d
         WHERE h.ORD_DT BETWEEN :v_from AND :v_last
           AND d.ORD_NO = h.ORD_NO
         GROUP BY d.ITM_NO) o
    ON o.ITM_NO = s.ITM_NO
  FULL OUTER JOIN
       (SELECT ITM_NO, SUM(INV_QTY) AS qty
          FROM CF18
         WHERE INV_BS_DT = :v_last
         GROUP BY ITM_NO) k
    ON k.ITM_NO = s.ITM_NO

실행계획 학습용 예시 ​

xplan
[SELECT만 따로 돌린 계획, 129줄]
-----------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |          |      1 |        |    129 |00:00:00.04 |     967 |
|   1 |  VIEW                                 | VW_FOJ_1 |      1 |    129 |    129 |00:00:00.04 |     967 |
|*  2 |   HASH JOIN FULL OUTER                |          |      1 |    129 |    129 |00:00:00.04 |     967 |
|   3 |    VIEW                               |          |      1 |     68 |     68 |00:00:00.01 |       6 |
|   4 |     HASH GROUP BY                     |          |      1 |     68 |     68 |00:00:00.01 |       6 |
|*  5 |      TABLE ACCESS FULL                | CF18     |      1 |    136 |    136 |00:00:00.01 |       6 |
|   6 |    VIEW                               | VW_FOJ_0 |      1 |    108 |    108 |00:00:00.04 |     961 |
|*  7 |     HASH JOIN FULL OUTER              |          |      1 |    108 |    108 |00:00:00.04 |     961 |
|   8 |      VIEW                             |          |      1 |     47 |     47 |00:00:00.01 |      67 |
|   9 |       HASH GROUP BY                   |          |      1 |     47 |     47 |00:00:00.01 |      67 |
|* 10 |        HASH JOIN                      |          |      1 |    600 |    604 |00:00:00.01 |      67 |
|* 11 |         TABLE ACCESS FULL             | CF12     |      1 |     61 |     62 |00:00:00.01 |      10 |
|  12 |         TABLE ACCESS FULL             | CF13     |      1 |   7102 |   7102 |00:00:00.01 |      57 |
|  13 |      VIEW                             |          |      1 |     88 |     88 |00:00:00.03 |     894 |
|  14 |       HASH GROUP BY                   |          |      1 |     88 |     88 |00:00:00.03 |     894 |
|  15 |        TABLE ACCESS BY INDEX ROWID    | CF11     |      1 |  45980 |  46322 |00:00:00.02 |     894 |
|* 16 |         INDEX RANGE SCAN              | CF11_PK  |      1 |  45980 |  46322 |00:00:00.01 |     181 |
-----------------------------------------------------------------------------------------------------------
   2 - access("K"."ITM_NO"="S"."ITM_NO")
   5 - filter("INV_BS_DT"=:V_LAST)
   7 - access("O"."ITM_NO"="S"."ITM_NO")
  10 - access("D"."ORD_NO"="H"."ORD_NO")
  11 - filter(("H"."ORD_DT">=:V_FROM AND "H"."ORD_DT"<=:V_LAST))
  16 - access("SLE_NO">=:V_FROM AND "SLE_NO"<:V_TO)

문제 ​

  1. SELECT가 129줄을 내는 이유를 세 집합의 품목 수(판매 88, 발주 47, 재고 68)로 설명하고, ORA-01400을 일으킨 행이 무엇인지 쓰시오.
  2. 세 가지 고침을 SQL로 쓰고 비교하시오. ① 두 번째 ON과 키를 COALESCE로 고친 FULL OUTER JOIN ② UNION ALL로 쌓아 품목별로 묶기 ③ CF01을 기준으로 세 집합을 LEFT JOIN. 결과 줄 수, 읽는 블록, 부작용을 쓰시오.
  3. 채택할 안을 정하고, 배치가 맞는 결과를 넣었는지 확인하는 대사 방법을 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자