Update with 2 joins

会有一股神秘感。 提交于 2021-02-16 20:32:06

问题


I'm trying to update data in 2 distinct tables in the same query with the following code:

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';

But I'm getting this error: Cannot modify a column which maps to a non key-preserved table. What am I doing wrong?


回答1:


A view with a join (or an inline view containing a join in your case) must meet the following conditions to be updatable:
https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_8004.htm

If you want a join view to be updatable, then all of the following conditions must be true:

The DML statement must affect only one table underlying the join.

For an INSERT statement, the view must not be created WITH CHECK OPTION, and all columns into which values are inserted must come from a key-preserved table. A key-preserved table is one for which every primary key or unique key value in the base table is also unique in the join view.

For an UPDATE statement, the view must not be created WITH CHECK OPTION, and all columns updated must be extracted from a key-preserved table.


The first condition is rather obvious: The DML statement must affect only one table underlying the join.

But what does it mean: "key preserved table" ?

A key-preserved table is one for which every primary key or unique key value in the base table is also unique in the join view.

The key preserved table means that each row from this table will appear at most once in the result of a view.

Consider a simple example:

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;

And a join:

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  

If you look at a result of this join, it is apparent that:

  • user_id, user_name and age come from non-key preserved table
  • email comes from key-preserved table

And now: this update is acceptable, because it updates a key preserved column (table) in this join:

    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 

But this update cannot be done, since it touches a column from non-key preserved table:

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.

If you think a while .... Tom appears 2 times in the result of the join (but there is only one Tom in the users table).

When we are trying to update age = age + 2 in this join, then what should be a result of this update ?

Should Tom be updated only once ? Should Tom be 22+2 = 24 years old after this update ?

Or maybe Tom should be updated twice (since it appears twice in the result of the join) so it should be 22 + 2 + 2 = 26 years old.

Another example - please tell me what should be an outcome of this update?:

UPDATE ( ....our join ... ) SET age = length( email );

There are very difficult questions :)
And because of this Oracle prevents from updating non-key preserved tables.


The error message gives this hint: *Action: Modify the underlying base tables directly.

This means that we must update this table directly, using a separate UPDATE command:

  UPDATE users SET age = age + 2


来源:https://stackoverflow.com/questions/36231754/update-with-2-joins

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!