365

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';
Alexis Dufrenoy
  • 11,784
  • 12
  • 82
  • 124
user169743
  • 4,067
  • 5
  • 19
  • 14

15 Answers15

506

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

Barbaros Özhan
  • 59,113
  • 10
  • 31
  • 55
Tony Andrews
  • 129,880
  • 21
  • 220
  • 259
  • 1
    I'am writing similar query portable between dbms: oracle considers inline view is updatable, but mysql not – jonny Feb 02 '11 at 13:47
  • 6
    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, thanks – Gustavo Rubio Jun 06 '11 at 02:25
  • 56
    The second example has the benefit of allowing you to test the SQL before actually performing the update. – Daniel Reis Jan 19 '12 at 10:18
  • 11
    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. – nemo May 02 '12 at 00:03
  • 1
    Worked for me but with a different set statement. `set value = code` – Alejandro Bastidas Mar 11 '13 at 15:37
  • Does not work with Oracle 11.2g but Quassnoi's solution works. – Michael-O May 06 '13 at 12:11
  • 13
    Explanation on key-preserved requirement for updatable joins: https://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:548422757486 – Vadzim Feb 12 '15 at 12:17
  • 4
    Got this - "ORA-01779: cannot modify a column which maps to a non key-preserved table", and then http://stackoverflow.com/questions/9335536/oracle-update-join-non-key-preserved-table helped. – Swapnil Sep 16 '15 at 06:30
  • 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`. – Dzmitry Lahoda Jun 08 '16 at 08:28
  • 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). – user2171669 Jul 20 '18 at 21:15
  • 1
    I'm getting this error, "ORA-01779: cannot modify a column which maps to a non key-preserved table. – Marshall Dec 26 '18 at 16:07
  • 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. – Alain May 09 '19 at 22:24
  • The second one worked but only once I added primary keys to `table2` – jryan14ify Nov 09 '22 at 22:39
238

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;
Quassnoi
  • 413,100
  • 91
  • 616
  • 614
  • 2
    Works perfectly but Oracle required me to say `merge into table 1 t` and so forth. – Michael-O May 06 '13 at 12:12
  • 1
    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... – Marc Jul 21 '14 at 19:02
  • Just a point here, if your key (trg.rowid or src.rid) has one duplicated item this clause throw an error: http://ora-30926.ora-code.com/ – Henrique Apr 25 '16 at 12:25
  • @Marc 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. – vapcguy Jul 22 '16 at 20:32
  • @Quassnoi 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? – supernova Jan 15 '18 at 03:52
  • 1
    @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. – Quassnoi Jan 15 '18 at 13:03
  • @Sonny: Oracle older versions requires `WHEN NOT MATCHED THEN` clause to be present – Aswin Alagappan May 17 '18 at 16:16
  • 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. –  Nov 28 '18 at 13:00
  • This is the best answer!!! Using MERGE INTO it is faster then other constructs. Here there's a link to two executable examples https://www.simboli.eu/p.php?URL_short=ORACLE_UPDATE_WITH_JOIN_TABLE – Nicolaesse Feb 02 '22 at 14:16
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.

Roland
  • 7,525
  • 13
  • 61
  • 124
  • 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.) – sf_jeff Sep 29 '19 at 15:46
19

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.

duvo
  • 1,634
  • 2
  • 18
  • 30
14
 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
Morten Anderson
  • 2,301
  • 15
  • 20
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.

Alexis Dufrenoy
  • 11,784
  • 12
  • 82
  • 124
4

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
NASSER
  • 5,900
  • 7
  • 38
  • 57
  • 1
    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! – vapcguy Jul 20 '16 at 21:56
  • This answer adds nothing to the answer already provided by Quassnoi five years before yours. – Forage Nov 14 '17 at 14:18
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;
Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
Hemant
  • 49
  • 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. – vapcguy Jul 22 '16 at 21:03
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'
;
Janek Bogucki
  • 5,033
  • 3
  • 30
  • 40
1
UPDATE table1 t1
SET t1.value = 
    (select t2.CODE from table2 t2 
     where t1.value = t2.DESC) 
WHERE t1.UPDATETYPE='blah';
afnhsn
  • 21
  • 1
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.

Phil3992
  • 1,059
  • 6
  • 21
  • 45
  • 1
    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? – Noel Widmer May 30 '17 at 09:52
  • 1
    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. – Dan Anderson May 30 '17 at 11:31
  • 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". – sf_jeff Sep 29 '19 at 15:49
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`
0

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;
/
Edu Castrillon
  • 527
  • 1
  • 12
  • 28
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.

Rob
  • 2,363
  • 7
  • 36
  • 54
-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
             )
Athafoud
  • 2,898
  • 3
  • 40
  • 58
test
  • 11