更新为2个连接

时间:2016-03-26 04:53:00

标签: oracle sql-update inner-join

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

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。我做错了什么?

1 个答案:

答案 0 :(得分:1)

带有连接的视图(或包含连接的内联视图)必须满足以下条件才能更新:
https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_8004.htm

  

如果您希望连接视图可更新,则以下所有内容   条件必须为真:

     

DML语句必须只影响联接的一个表。

     

对于INSERT语句,不能使用CHECK创建视图   OPTION以及插入值的所有列必须来自   一张钥匙保存的桌子。保存密钥的表是每个表   基表中的主键或唯一键值也是唯一的   加入视图。

     

对于UPDATE 语句,不得使用WITH CHECK创建视图   OPTION和更新的所有列必须从密钥保留中提取   表

第一个条件相当明显: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  

如果查看此联接的结果,很明显:

  • 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.

如果你想一会儿......汤姆在联接的结果中出现2次(但users表中只有一个汤姆)。

当我们尝试在此联接中更新age = age + 2时,此更新的结果应该是什么?

Tom应该只更新一次吗? 这次更新后Tom应该是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