有效的例子和無用的例子

create table tgt ( id, val ) as 
  select 1, 'a' from dual union all
  select 2, 'b' from dual
;

Table TGT created.

create table src ( id, val ) as 
  select 1, 'x' from dual union all
  select 2, 'y' from dual
;

Table SRC created.

update
  ( select t.val as t_val, s.val as s_val
    from   tgt t inner join src s on t.id = s.id
  )
set t_val = s_val
;

SQL Error: ORA-01779: cannot modify a column which maps to a non key-preserved table
01779. 00000 -  "cannot modify a column which maps to a non key-preserved table"
*Cause:    An attempt was made to insert or update columns of a join view which
           map to a non-key-preserved table.
*Action:   Modify the underlying base tables directly.

想象一下,如果我們在 src.id 列中的值 1 不止一次,src.val 的值不同,會發生什麼。顯然,更新沒有意義(在任何資料庫中 - 這是一個邏輯問題)。現在,我們知道 src.id 中沒有重複項,但 Oracle 引擎並不知道 - 所以它在抱怨。也許這就是為什麼這麼多從業者認為甲骨文“沒有加入 UPDATE”的原因?

Oracle 期望的是 src.id 應該是唯一的,並且它,Oracle,事先就會知道。輕鬆修復! 請注意,如果更新的匹配需要使用多個列,則同樣適用於複合鍵(在多個列上)。在實踐中,src.id 可能是 PK 而 tgt.id 可能是指向此 PK 的 FK,但這與使用 join 的更新無關; 什麼相關的是唯一的約束。

alter table src add constraint src_uc unique (id);

Table SRC altered.

update
  ( select t.val as t_val, s.val as s_val
    from   tgt t inner join src s on t.id = s.id
  )
set t_val = s_val
;

2 rows updated.

select * from tgt;

ID  VAL
--  ---
 1  x
 2  y

使用 MERGE 語句可以獲得相同的結果(值得自己的文件文章),在這些情況下我個人更喜歡 MERGE,但原因並不是“Oracle 不會使用連線進行更新”。如此示例所示,Oracle 確實使用聯接進行更新。