使用2个联接进行更新

gudnpqoy  于 2021-07-26  发布在  Java
关注(0)|答案(1)|浏览(271)

我尝试用以下代码更新同一查询中两个不同表中的数据:

UPDATE (SELECT s.passNumb, a.hour, a.minute, p.number, s.situation
        FROM passwords s
        JOIN atend a ON s.passNumb = a.passNumb
        JOIN points p ON a.number = p.number
        WHERE s.passNumb = 1 AND 
              p.number = 1)
  SET a.hour = TO_CHAR(SYSDATE, 'HH24'),
      a.minute = TO_CHAR(SYSDATE, 'MI'),
      s.situation = 'F';

但我得到了一个错误: Cannot modify a column which maps to a non key-preserved table . 我做错什么了?

gojuced7

gojuced71#

具有联接的视图(或在本例中包含联接的内联视图)必须满足以下条件才能更新:
https://docs.oracle.com/cd/b28359_01/server.111/b28286/statements_8004.htm
如果希望联接视图可更新,则必须满足以下所有条件:
dml语句必须只影响连接下面的一个表。
对于insert语句,不能使用check选项创建视图,并且插入值的所有列必须来自键保留表。键保留表是基表中的每个主键或唯一键值在联接视图中也是唯一的表。
对于update语句,不能使用check选项创建视图,必须从保留键的表中提取所有更新的列。
第一个条件很明显: The DML statement must affect only one table underlying the join. 但“键保存表”是什么意思呢?
键保留表是基表中的每个主键或唯一键值在联接视图中也是唯一的表。
键保留表意味着该表中的每一行在视图的结果中最多出现一次。
举个简单的例子:

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

如果查看此联接的结果,很明显:
用户id、用户名和年龄来自非键保留表
电子邮件来自密钥保留表
现在:此更新是可以接受的,因为它更新了此联接中的键保留列(表):

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.

如果你想一想。。。。在join的结果中,tom出现了两次(但是在join中只有一个tom) users 表)。
当我们试图更新 age = age + 2 在这个连接中,那么这个更新的结果应该是什么?
汤姆应该只更新一次吗?更新后,tom应该是22+2=24岁吗?
或者tom应该更新两次(因为它在join的结果中出现了两次),所以应该是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

相关问题