Update statement with inner join on Oracle
Asked Answered
A

16

377

I have a query which works fine in MySQL, but when I run it on Oracle I get the following error:

SQL Error: ORA-00933: SQL command not properly ended
00933. 00000 - "SQL command not properly ended"

The query is:

UPDATE table1
INNER JOIN table2 ON table1.value = table2.DESC
SET table1.value = table2.CODE
WHERE table1.UPDATETYPE='blah';
Antidepressant answered 15/3, 2010 at 11:40 Comment(2)
When I tried to setup table2 in Oracle to test my answer I found that Oracle rejected DESC as a column name.Clarisclarisa
Sorry I just abbreviated the original column name to desc its obviously not that in the dbAntidepressant
E
520

That syntax isn't valid in Oracle. You can do this:

UPDATE table1 SET table1.value = (SELECT table2.CODE
                                  FROM table2 
                                  WHERE table1.value = table2.DESC)
WHERE table1.UPDATETYPE='blah'
AND EXISTS (SELECT table2.CODE
            FROM table2 
            WHERE table1.value = table2.DESC);

Or you might be able to do this:

UPDATE 
(SELECT table1.value as OLD, table2.CODE as NEW
 FROM table1
 INNER JOIN table2
 ON table1.value = table2.DESC
 WHERE table1.UPDATETYPE='blah'
) t
SET t.OLD = t.NEW

It depends if the inline view is considered updateable by Oracle ( To be updatable for the second statement depends on some rules listed here ).

Erlanger answered 15/3, 2010 at 11:53 Comment(13)
I'am writing similar query portable between dbms: oracle considers inline view is updatable, but mysql notNutt
I did the second example but had to add aliases to the column names in the select and then reference them by their names in the SET but it worked, thanksEquivalency
The second example has the benefit of allowing you to test the SQL before actually performing the update.Asir
The second example worked for me. I like that one because it looks clean and readable. Don't know what the pros and cons are between the two when it comes to performance. But, I wasn't worried about that for now 'cuz I used this for a one off script to correct bad data.Equerry
Worked for me but with a different set statement. set value = codePomfrey
Does not work with Oracle 11.2g but Quassnoi's solution works.Openeyed
Explanation on key-preserved requirement for updatable joins: asktom.oracle.com/pls/asktom/…Japonica
Got this - "ORA-01779: cannot modify a column which maps to a non key-preserved table", and then #9336036 helped.Cumings
I had similar case, but wanted to update table1.value = 'Single Value'. In this case EXISTS becomes any of instead for each, hence updates things which should not be updated. I used second instead. Both queries work in 11.2.Latishalatitude
This is basically a simple trick using ALIASES (that's a powerful tool). You can put as many joins in inner select as you want. And the exists clause (which is the same as inner select) needed to ensure assigned value presence (otherwise will be null I suppose).Consort
I'm getting this error, "ORA-01779: cannot modify a column which maps to a non key-preserved table.Junette
The first one worked for us, but we had to include our filter in the first as well as in the second parentheses because the filter was to be applied on table 2 instead of table 1 in our case.Religiose
The second one worked but only once I added primary keys to table2Colicroot
D
242

Use this:

MERGE
INTO    table1 trg
USING   (
        SELECT  t1.rowid AS rid, t2.code
        FROM    table1 t1
        JOIN    table2 t2
        ON      table1.value = table2.DESC
        WHERE   table1.UPDATETYPE='blah'
        ) src
ON      (trg.rowid = src.rid)
WHEN MATCHED THEN UPDATE
    SET trg.value = code;
Deviationism answered 15/3, 2010 at 11:58 Comment(9)
Works perfectly but Oracle required me to say merge into table 1 t and so forth.Openeyed
Late to the party, but this is still a good thread. I need to know, tho'... did I miss something? Master table, "table1". In the USING, table1 aliased as t1. Table2, aliased as t2, but in the ON, the references are... ? External Table1 - not t1 - is this a reference to the outer table or a type? Table2? Not t2? Je suis confused. Fan of better aliases...Antefix
Just a point here, if your key (trg.rowid or src.rid) has one duplicated item this clause throw an error: ora-30926.ora-code.comBumgardner
@Antefix In the ON, trg is the alias for the master table, table1 ("outer" table by your logic), and src references the USING group ("inner table" by your logic). But yeah, probably could've been referenced better, but I was able to follow it.Yordan
@Deviationism You answered "Oracle does not support joins in the UPDATE statements" but Tony Andrews 2nd answer above shows using join within update statement and couple of guys tested it's working! What's the catch here?Systematics
@supernova: tony's answer is updating an inline view. This can work in some cases, but the view has to be "key-preserved" (every joined table has to be equality-joined on its primary key or otherwise unique fieldset). This makes sure every record in the target table contributes to at most one record in the resulting rowset, and, hence, every record in the target table is updated at most once.Deviationism
@Sonny: Oracle older versions requires WHEN NOT MATCHED THEN clause to be presentGrainger
Oracle does support joins in UPDATE statement, you just have to use the correct syntax. I edited your Answer to remove the false statement to the contrary.Magnetomotive
This is the best answer!!! Using MERGE INTO it is faster then other constructs. Here there's a link to two executable examples simboli.eu/p.php?URL_short=ORACLE_UPDATE_WITH_JOIN_TABLEArgentina
B
40

MERGE with WHERE clause:

MERGE into table1
USING table2
ON (table1.id = table2.id)
WHEN MATCHED THEN UPDATE SET table1.startdate = table2.start_date
WHERE table1.startdate > table2.start_date;

You need the WHERE clause because columns referenced in the ON clause cannot be updated.

Butane answered 7/5, 2015 at 14:12 Comment(1)
This version is arguably cleaner, but it is not trigger friendly because there is no way I am aware of to avoid triggering update triggers for unchanged rows using this syntax. (I am assuming that the triggers are needed for the changed rows.)Stipule
F
22

Do not use some of the answers above.

Some suggest the use of nested SELECT, don't do that, it is excruciatingly slow. If you have lots of records to update, use join, so something like:

update (select bonus 
        from employee_bonus b 
        inner join employees e on b.employee_id = e.employee_id 
        where e.bonus_eligible = 'N') t
set t.bonus = 0;

See this link for more details. http://geekswithblogs.net/WillSmith/archive/2008/06/18/oracle-update-with-join-again.aspx.

Also, ensure that there are primary keys on all the tables you are joining.

Fairminded answered 4/4, 2017 at 22:36 Comment(2)
Short n PerfectDebora
SQL Error: ORA-01779: cannot modify a column which maps to a non key-preserved tableElizabetelizabeth
F
15
 UPDATE ( SELECT t1.value, t2.CODE
          FROM table1 t1
          INNER JOIN table2 t2 ON t1.Value = t2.DESC
          WHERE t1.UPDATETYPE='blah')
 SET t1.Value= t2.CODE
Freberg answered 15/3, 2010 at 11:58 Comment(1)
SQL Error: ORA-01779: cannot modify a column which maps to a non key-preserved tableElizabetelizabeth
S
7

As indicated here, the general syntax for the first solution proposed by Tony Andrews is :

update some_table s
set   (s.col1, s.col2) = (select x.col1, x.col2
                          from   other_table x
                          where  x.key_value = s.key_value
                         )
where exists             (select 1
                          from   other_table x
                          where  x.key_value = s.key_value
                         )

I think this is interesting especially if you want update more than one field.

Scoggins answered 14/1, 2016 at 12:37 Comment(1)
This doesn't work for me. It updates the entire table.Celik
B
5

It works fine oracle

merge into table1 t1
using (select * from table2) t2
on (t1.empid = t2.empid)
when matched then update set t1.salary = t2.salary
Barbarize answered 4/9, 2015 at 4:40 Comment(2)
Can set multiple properties by adding a comma at the end of that. I needed to do t1.First_Name = t2.FirstName, t1.Last_Name = t2.LastName on a table after matching it on the "UserName" column (t1.UserName = t2.UserName) to retrieve their name from a table called UserInfo (select * from UserInfo) t2). The database was such where it was using UserName as a primary key to UserInfo everywhere, instead of placing FirstName and LastName in the table, directly. This fixed that!Yordan
This answer adds nothing to the answer already provided by Quassnoi five years before yours.Trypsin
N
3

This following syntax works for me.

UPDATE
(SELECT A.utl_id,
    b.utl1_id
    FROM trb_pi_joint A
    JOIN trb_tpr B
    ON A.tp_id=B.tp_id Where A.pij_type=2 and a.utl_id is null
)
SET utl_id=utl1_id;
Novella answered 24/1, 2013 at 5:18 Comment(1)
@JimGarrison Please re-edit this answer so I can remove my downvote.... I was trying to use this syntax and it wasn't updating my table. I found out why - my SET was doing a REPLACE and I was trying to blank a particular string in the column - turns out Oracle treats '' as null, and this field could not be nulled. I thought the syntax was merely updating a temp table instead of the real one, but I was wrong.Yordan
C
2

Using description instead of desc for table2,

update
  table1
set
  value = (select code from table2 where description = table1.value)
where
  exists (select 1 from table2 where description = table1.value)
  and
  table1.updatetype = 'blah'
;
Clarisclarisa answered 15/3, 2010 at 12:4 Comment(1)
why u want to fire two separate queries on table2Wundt
M
1
UPDATE table1 t1
SET t1.value = 
    (select t2.CODE from table2 t2 
     where t1.value = t2.DESC) 
WHERE t1.UPDATETYPE='blah';
Morrill answered 5/6, 2014 at 7:59 Comment(0)
B
1
UPDATE (SELECT T.FIELD A, S.FIELD B
FROM TABLE_T T INNER JOIN TABLE_S S
ON T.ID = S.ID)
SET B = A;

A and B are alias fields, you do not need to point the table.

Berns answered 30/5, 2017 at 9:47 Comment(3)
Hi Dan. You are posting to a pretty old question that already has very good answers. Can you explain when you question is preferable over the other solutions?Sanfordsanfourd
Of course, I've seen an answer where b = a were written by pointing the table name (table1.B = table2.A) but there's no need to point the table.Berns
You are actually updating fields from the view, which get mapped to the table. If the inner view were aliased h, then the "self-documenting" version would be "set h.b = h.a".Stipule
O
1

Just as a matter of completeness, and because we're talking Oracle, this could do it as well:

declare
begin
  for sel in (
    select table2.code, table2.desc
    from table1
    join table2 on table1.value = table2.desc
    where table1.updatetype = 'blah'
  ) loop
    update table1 
    set table1.value = sel.code
    where table1.updatetype = 'blah' and table1.value = sel.desc;    
  end loop;
end;
/
Oilbird answered 19/2, 2019 at 8:58 Comment(1)
This could do it, but it's about the slowest way possible.Hallerson
R
0
UPDATE IP_ADMISSION_REQUEST ip1
SET IP1.WRIST_BAND_PRINT_STATUS=0
WHERE IP1.IP_ADM_REQ_ID        =
  (SELECT IP.IP_ADM_REQ_ID
  FROM IP_ADMISSION_REQUEST ip
  INNER JOIN VISIT v
  ON ip.ip_visit_id=v.visit_id
  AND v.pat_id     =3702
  ); `enter code here`
Rudich answered 16/12, 2015 at 9:53 Comment(0)
C
0

Oracle base has a good run down on this.

https://oracle-base.com/articles/misc/updates-based-on-queries

From this link - I used a modification of the above query which did not work for me (the answer from mathguy which uses rowid)

MERGE /*+ APPEND PARALLEL(8) */ INTO dest_table tt
USING source_table st
ON (tt.identifier = st.identifier)
WHEN MATCHED THEN
  UPDATE SET tt.number = st.number;

Here I have two tables: source and dest. They both have a varchar field in common and I am adding the source identify field (PK) into the dest table.

Christalchristalle answered 9/11, 2020 at 13:51 Comment(0)
U
0

Oracle Database 23c has added support for direct joins in update and delete:

select employee_id, salary from hr.employees 
where  job_id = 'ST_MAN';

EMPLOYEE_ID     SALARY
----------- ----------
        120       8000
        121       8200
        122       7900
        123       6500
        124       5800

update hr.employees e
set    salary = max_salary 
from   hr.jobs j
where  j.job_id = e.job_id;

select employee_id, salary from hr.employees 
where  job_id = 'ST_MAN';

EMPLOYEE_ID     SALARY
----------- ----------
        120       8500
        121       8500
        122       8500
        123       8500
        124       8500
Uraemia answered 16/2 at 18:15 Comment(0)
C
-3
update table1  a 
   set a.col1='Y' 
 where exists(select 1 
                from table2 b
               where a.col1=b.col1 
                 and a.col2=b.col2
             )
Compete answered 27/11, 2015 at 15:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.