Updating Oracle tables using a join

Updating Oracle tables using a join

Doing a 'join update' in Oracle can sometime cause a few headaches. By 'join update' I mean the situation where we have two tables and want to update some of the rows in the first table based on values that exist in the second table.


Imagine this scenario :

drop table t;
drop table s;
create table t (id number, name varchar2(32));
create table s (id number, name varchar2(32));
 
insert into t values (1,'1');
insert into t values (2,'2');
insert into t values (3,'3');
insert into t values (4,'4');
insert into t values (5,'5');
 
insert into s values (3,'three');
insert into s values (5,'five');
select * from t;
update t
   set name = (select name
                 from s
                where t.id = s.id);
select * from t;

Our first table (t) simply has the numbers 1-5 in the 'id' and 'name' column but we want to update some of the names based on the contents of the second table (s) but this table only has values for two of the rows.  This is OK so long as we can preserve the 'default' numeric values.  However the script above gives the following output :

drop table t succeeded.
drop table s succeeded.
create table succeeded.
create table succeeded.
1 rows inserted
1 rows inserted
1 rows inserted
1 rows inserted
1 rows inserted
1 rows inserted
1 rows inserted

ID                     NAME                             
---------------------- -------------------------------- 
1                      1                                
2                      2                                
3                      3                                
4                      4                                
5                      5                                

5 rows selected

5 rows updated

ID                     NAME                             
---------------------- -------------------------------- 
1                                                       
2                                                       
3                      three                            
4                                                       
5                      five                             
 
5 rows selected

The problem here is that the update isn't a true 'join' as every row is going to be updated and if the 'join' fails then a null is written.  The traditional solution to this is to rewrite the SQL to be something like :

update t
   set name = (select name
                 from s
                where t.id = s.id)
 where exists (select 1
                 from s
                where t.id = s.id);
select * from t;

which does the job correctly :

2 rows updated
ID                     NAME 
---------------------- -------------------------------- 
1                      1 
2                      2 
3                      three
4                      4 
5                      five
 
5 rows selected

However this isn't a very good solution for updates involving very large tables as it can significantly slow things down.  One alternative is the MERGE statement :

merge into t
 using (select id, name
          from s) s
    on (t.id = s.id)
 when matched then
   update set t.name = s.name;

select * from t;
2 rows merged
ID                     NAME 
---------------------- -------------------------------- 
1                      1 
2                      2 
3                      three
4                      4 
5                      five
5 rows selected

An other option is to use the NVL() function which allows you to deal with the problem of the nulls above :

update t
   set name = nvl((select name
                    from s
                   where t.id = s.id), t.name);

select * from t;

which also works :

5 rows updated
ID                     NAME 
---------------------- -------------------------------- 
1                      1
2                      2 
3                      three 
4                      4 
5                      five
5 rows selected

Hope this helps!


Comments (0)


Add a Comment

Please login to comment.