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

배송 완료를 주문에 반영하는 조인 뷰 UPDATE가 ORA-01779로 막힌다

중급UPDATE이커머스447 / 700

이 문제는 교재 18화 졸업 과제와 같은 이커머스 데이터베이스를 씁니다. 『튜너의 1년』을 순서대로 읽고 있다면 졸업 과제를 먼저 풀고 오세요. 아래 「테이블과 인덱스」 칸에 과제의 답 일부(제약 조건, 인덱스, 통계의 실제 상태)가 나옵니다.

상황 ​

18화 진단 뒤 정합성 정리 작업에서, 배송 테이블(shipping)로는 배송이 끝났는데 주문(orders.status)은 아직 'SHIPPED'로 남은 주문 2,816건을 바로잡기로 했다. 지금 SQL은 4초 걸린다. 같은 정리를 매일 밤 돌릴 계획이라 개발자가 조인 뷰 UPDATE로 바꿔 보았는데 ORA-01779로 막혔다. 개발자: "설계서에 shipping과 orders는 1:1이라고 적혀 있는데요."

테이블과 인덱스 ​

  • orders: 34,908행 · 193블록 · PK(order_id) · status 6종 · 'SHIPPED' 3,410건
  • shipping: 34,908행 · 183블록 · PK(shipping_id)만 있다 · order_id에는 제약도 인덱스도 없다(설계서상 1:1) · status는 'READY', 'SHIPPED', 'DELIVERED', 'RETURNED'
  • shipping이 'DELIVERED'나 'RETURNED'인 행은 29,870행이다. 정리 대상(주문은 'SHIPPED', 배송은 끝남)은 2,816건이다.
  • FK는 모두 꺼져 있다(DISABLED). 통계는 최신이다.

원본 SQL ​

sql
UPDATE orders o
   SET o.status = (SELECT s.status FROM shipping s WHERE s.order_id = o.order_id)
 WHERE o.status = 'SHIPPED'
   AND EXISTS (SELECT 1 FROM shipping s
                WHERE s.order_id = o.order_id
                  AND s.status IN ('DELIVERED', 'RETURNED'));

-- 개발자가 바꿔 본 조인 뷰 → ORA-01779: cannot modify a column which maps to a non key-preserved table
-- UPDATE (SELECT o.status AS o_status, s.status AS s_status
--           FROM orders o, shipping s
--          WHERE s.order_id = o.order_id
--            AND o.status = 'SHIPPED'
--            AND s.status IN ('DELIVERED', 'RETURNED'))
--    SET o_status = s_status;

실행계획 학습용 예시 ​

xplan
----------------------------------------------------------------------------------------
| Id  | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT     |          |      1 |        |      0 |00:00:04.12 |    521K |
|   1 |  UPDATE              | orders   |      1 |        |      0 |00:00:04.12 |    521K |
|*  2 |   HASH JOIN SEMI     |          |      1 |   3410 |   2816 |00:00:00.02 |     376 |
|*  3 |    TABLE ACCESS FULL | orders   |      1 |   5818 |   3410 |00:00:00.01 |     193 |
|*  4 |    TABLE ACCESS FULL | shipping |      1 |  29870 |  29870 |00:00:00.01 |     183 |
|*  5 |   TABLE ACCESS FULL  | shipping |   2816 |      1 |   2816 |00:00:04.08 |    515K |
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("S"."ORDER_ID"="O"."ORDER_ID")
   3 - filter("O"."STATUS"='SHIPPED')
   4 - filter(("S"."STATUS"='DELIVERED' OR "S"."STATUS"='RETURNED'))
   5 - filter("S"."ORDER_ID"=:B1)

문제 ​

  1. Buffers 521K가 어디서 나오는지 실행계획의 Starts로 설명하시오.
  2. 데이터가 1:1인데도 조인 뷰 UPDATE가 ORA-01779로 막힌 이유를 설명하시오.
  3. 조인 뷰 UPDATE가 동작하도록 고치시오. 고치기 전에 확인할 것과 그 조치의 대가도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자