Skip to content
튜닝 연습DML과 배치 튜닝
DML-027

6월 단가 소급 조인 뷰가 ORA-01779로 막혀 서브쿼리 UPDATE로 바꿨더니 1만 5천 행을 고친다

중급MERGE유통452 / 700

상황 ​

본사 레거시 유통은 2023년 6월 1일부로 판매가를 바꿨다(상품 38개). 그런데 POS 세 대가 6월 1일 ~ 10일 판매를 옛 단가로 보내서, 판매상품(CF11)의 판매단가(SLE_UPRC)를 상품가격 이력(CF05)의 6월 1일 기준 단가로 소급해 고친다. 처음에는 조인 뷰 UPDATE를 썼는데 ORA-01779로 막혔다. 인터넷에서 찾은 BYPASS_UJVC 힌트를 붙여도 마찬가지였다. 그래서 지금은 아래의 서브쿼리 UPDATE로 돌린다. 매장 정산 담당: "고친 행이 15,301건이라는데, 옛 단가로 들어온 건 4천 건쯤이에요."

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · CF10_PK (SLE_NO) · IX_CF10_SLE_DT (SLE_DT) · 6월 1일 ~ 10일 판매 6,984건
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) 높이 3 · 6월 1일 ~ 10일분 15,301행, 그중 옛 단가로 들어온 행 4,112행
  • CF05 상품가격: 157행 · 5블록 · PK (ITM_NO, UPRC_KN_CD, APL_DT)(CF05_PK, 리프 블록 하나) · 판매가(UPRC_KN_CD = '01') 이력은 상품마다 1 ~ 3행 · 6월 2일 ~ 10일에 시작하는 이력은 없다
  • 통계는 최신이다.

원본 SQL ​

sql
-- 처음 판 (조인 뷰) → ORA-01779 · BYPASS_UJVC 힌트를 붙여도 같다
-- UPDATE (SELECT d.SLE_UPRC, p.ITM_UPRC
--           FROM CF10 h, CF11 d, CF05 p
--          WHERE h.SLE_DT BETWEEN '20230601' AND '20230610'
--            AND d.SLE_NO = h.SLE_NO
--            AND p.ITM_NO = d.ITM_NO
--            AND p.UPRC_KN_CD = '01'
--            AND p.APL_DT <= '20230601')
--    SET SLE_UPRC = ITM_UPRC;

-- 지금 판
UPDATE CF11 d
   SET d.SLE_UPRC = (SELECT p.ITM_UPRC
                       FROM CF05 p
                      WHERE p.ITM_NO = d.ITM_NO
                        AND p.UPRC_KN_CD = '01'
                        AND p.APL_DT = (SELECT MAX(x.APL_DT)
                                          FROM CF05 x
                                         WHERE x.ITM_NO = d.ITM_NO
                                           AND x.UPRC_KN_CD = '01'
                                           AND x.APL_DT <= '20230601'))
 WHERE d.SLE_NO IN (SELECT h.SLE_NO FROM CF10 h
                     WHERE h.SLE_DT BETWEEN '20230601' AND '20230610');

실행계획 학습용 예시 ​

xplan
----------------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT                       |                |      1 |        |      0 |00:00:00.37 |   89742 |
|   1 |  UPDATE                                | CF11           |      1 |        |      0 |00:00:00.37 |   89742 |
|   2 |   NESTED LOOPS                         |                |      1 |  15300 |  15301 |00:00:00.04 |   21039 |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED | CF10           |      1 |   6980 |   6984 |00:00:00.01 |      87 |
|*  4 |     INDEX RANGE SCAN                   | IX_CF10_SLE_DT |      1 |   6980 |   6984 |00:00:00.01 |      22 |
|*  5 |    INDEX RANGE SCAN                    | CF11_PK        |   6984 |      2 |  15301 |00:00:00.02 |   20952 |
|   6 |   TABLE ACCESS BY INDEX ROWID          | CF05           |  15301 |      1 |  15301 |00:00:00.08 |   45903 |
|*  7 |    INDEX UNIQUE SCAN                   | CF05_PK        |  15301 |      1 |  15301 |00:00:00.06 |   30602 |
|   8 |     SORT AGGREGATE                     |                |  15301 |      1 |  15301 |00:00:00.03 |   15301 |
|   9 |      FIRST ROW                         |                |  15301 |      1 |  15301 |00:00:00.02 |   15301 |
|* 10 |       INDEX RANGE SCAN (MIN/MAX)       | CF05_PK        |  15301 |      1 |  15301 |00:00:00.02 |   15301 |
----------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   4 - access("H"."SLE_DT">='20230601' AND "H"."SLE_DT"<='20230610')
   5 - access("D"."SLE_NO"="H"."SLE_NO")
   7 - access("P"."ITM_NO"=:B1 AND "P"."UPRC_KN_CD"='01' AND "P"."APL_DT"=(SELECT MAX("X"."APL_DT") FROM "CF05" "X" ...))
  10 - access("X"."ITM_NO"=:B1 AND "X"."UPRC_KN_CD"='01' AND "X"."APL_DT"<='20230601')

  갱신된 행 15,301 (단가가 실제로 바뀐 행 4,112)

문제 ​

  1. 처음 판의 조인 뷰가 ORA-01779로 막힌 이유를 CF05의 키로 설명하시오. 막히지 않았다면(BYPASS_UJVC가 듣던 시절) 결과가 어떻게 되었을지도 쓰시오.
  2. 상품마다 한 행인 단가를 원천으로 MERGE 한 문장을 작성하고, 옛 단가로 들어온 행만 고치도록 하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자