具有连接的视图(或在您的情况下包含连接的内联视图)必须满足以下条件才能更新:
https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_8004.htm
如果您希望连接视图可更新,则以下所有内容
条件必须为真:
DML 语句必须只影响连接下的一张表。
对于 INSERT 语句,视图不能用 WITH CHECK 创建
OPTION,并且所有插入值的列必须来自
一个密钥保留的表。密钥保留表是每个
基表中的主键或唯一键值在
加入视图。
对于 UPDATE 语句,不能使用 WITH CHECK 创建视图
OPTION,并且所有更新的列都必须从保留的键中提取
表。
第一个条件比较明显:The DML statement must affect only one table underlying the join.
但它是什么意思:“key保留表”?
保留键的表是其中每个主键或唯一键
基表中的键值在连接视图中也是唯一的。
键保留表意味着该表中的每一行将在视图结果中最多出现一次。
考虑一个简单的例子:
CREATE TABLE users(
user_id int primary key,
user_name varchar(100),
age int
);
insert into users values(1,'Tom', 22);
CREATE TABLE emails(
user_id int,
email varchar(100)
);
Insert into emails values( 1, 'tom@somedomain.com' );
Insert into emails values( 1, 'tom@www.example.org' );
commit;
还有一个连接:
SELECT *
FROM users u
JOIN emails e ON u.user_id = e.user_id;
USER_ID USER_NAME AGE USER_ID EMAIL
---------- --------------- ---------- ---------- --------------------
1 Tom 22 1 tom@somedomain.com
1 Tom 22 1 tom@www.example.org
如果您查看此连接的结果,很明显:
- user_id、user_name 和 age 来自非键保留表
- 电子邮件来自密钥保留表
现在:这个更新是可以接受的,因为它更新了这个连接中的一个键保留列(表):
UPDATE (
SELECT * FROM users u
JOIN emails e ON u.user_id = e.user_id
)
SET email = email || '.it' ;
USER_ID USER_NAME AGE USER_ID EMAIL
---------- --------------- ---------- ---------- -------------------------
1 Tom 22 1 tom@somedomain.com.it
1 Tom 22 1 tom@www.example.org.it
但无法完成此更新,因为它涉及非键保留表中的列:
UPDATE (
SELECT * FROM users u
JOIN emails e ON u.user_id = e.user_id
)
SET age = age + 2;
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.
如果您想一想...... Tom 在连接结果中出现了 2 次(但 users 表中只有一个 Tom)。
当我们尝试在此连接中更新age = age + 2 时,此更新的结果应该是什么?
汤姆应该只更新一次吗?
更新后汤姆应该是 22+2 = 24 岁吗?
或者也许汤姆应该更新两次(因为它在连接结果中出现两次)所以它应该是 22 + 2 + 2 = 26 岁。
另一个例子 - 请告诉我这次更新的结果是什么?:
UPDATE ( ....our join ... ) SET age = length( email );
有非常难的问题:)
正因为如此,Oracle 阻止更新非键保留表。
错误消息给出了以下提示:
*Action: Modify the underlying base tables directly.
这意味着我们必须使用单独的 UPDATE 命令直接更新此表:
UPDATE users SET age = age + 2