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

판매 금액 칸을 상세 합계로 맞추는 조인 뷰 UPDATE가 ORA-01779로 막힌다

중급MERGE유통449 / 700

상황 ​

본사 레거시 유통의 판매(CF10)에는 화면 표시용으로 판매 금액(SLE_AMT)과 상품 종류 수(ITM_CNT) 칸이 있다. 6월 반품 정정으로 판매상품(CF11)의 수량이 바뀐 판매가 생겨서, 6월분 두 칸을 상세 합계로 다시 맞춘다. 지금 SQL은 2.4초에 Buffers 178K를 읽는다. 개발자가 조인 뷰 UPDATE로 바꾸려 했더니 ORA-01779가 났다. 개발자: "CF11에도 PK가 있고 CF10에도 PK가 있는데 왜 키 보존이 아니죠?"

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · CF10_PK (SLE_NO) · IX_CF10_SLE_DT (SLE_DT) · 2023년 6월 20,953건 · 이 문제에서는 SLE_AMT, ITM_CNT 칸이 더 있다(둘 다 NOT NULL)
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) 높이 3 · 6월분 45,912행(판매 한 건에 평균 2.2행)
  • 6월 판매는 모두 상세가 있다(확인함). 반품 정정으로 두 칸 값이 바뀌는 판매는 312건이다. 통계는 최신이다.

원본 SQL ​

sql
UPDATE CF10 h
   SET h.SLE_AMT = (SELECT SUM(d.SLE_QTY * d.SLE_UPRC) FROM CF11 d WHERE d.SLE_NO = h.SLE_NO),
       h.ITM_CNT = (SELECT COUNT(*)                    FROM CF11 d WHERE d.SLE_NO = h.SLE_NO)
 WHERE h.SLE_DT BETWEEN '20230601' AND '20230630';

-- 개발자가 바꿔 본 조인 뷰 → ORA-01779: cannot modify a column which maps to a non key-preserved table
-- UPDATE (SELECT h.SLE_AMT, d.SLE_QTY * d.SLE_UPRC AS AMT
--           FROM CF10 h, CF11 d
--          WHERE d.SLE_NO = h.SLE_NO
--            AND h.SLE_DT BETWEEN '20230601' AND '20230630')
--    SET SLE_AMT = AMT;

실행계획 학습용 예시 ​

xplan
---------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT                      |                |      1 |        |      0 |00:00:02.41 |    178K |
|   1 |  UPDATE                               | CF10           |      1 |        |      0 |00:00:02.41 |    178K |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED | CF10           |      1 |  20953 |  20953 |00:00:00.02 |     257 |
|*  3 |    INDEX RANGE SCAN                   | IX_CF10_SLE_DT |      1 |  20953 |  20953 |00:00:00.01 |      62 |
|   4 |   SORT AGGREGATE                      |                |  20953 |      1 |  20953 |00:00:00.71 |   83812 |
|   5 |    TABLE ACCESS BY INDEX ROWID BATCHED| CF11           |  20953 |      2 |  45912 |00:00:00.62 |   83812 |
|*  6 |     INDEX RANGE SCAN                  | CF11_PK        |  20953 |      2 |  45912 |00:00:00.31 |   62859 |
|   7 |   SORT AGGREGATE                      |                |  20953 |      1 |  20953 |00:00:00.33 |   62859 |
|*  8 |    INDEX RANGE SCAN                   | CF11_PK        |  20953 |      2 |  45912 |00:00:00.29 |   62859 |
---------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("H"."SLE_DT">='20230601' AND "H"."SLE_DT"<='20230630')
   6 - access("D"."SLE_NO"=:B1)
   8 - access("D"."SLE_NO"=:B1)

  갱신된 행 20,953 (값이 바뀐 판매 312)

문제 ​

  1. 조인 뷰 UPDATE가 ORA-01779로 막힌 이유를 두 테이블의 관계(1:M)로 설명하시오. 막히지 않았다면 결과가 어떻게 되었을지도 쓰시오.
  2. 판매 한 건마다 CF11을 두 번 찾아가지 않도록, CF11을 판매별로 먼저 묶어 한 문장으로 다시 작성하시오. 값이 바뀌는 판매만 고치도록 하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자