有效的例子和无用的例子

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 确实使用联接进行更新。