Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Dave.

Asked: May 24, 2004 - 5:02 pm UTC

Last updated: May 24, 2004 - 5:02 pm UTC

Version: 8.1.7

Viewed 1000+ times

You Asked

My understanding from the manuals is that I can use FOR UPDATE with a join cursor and potentially update columns in each table in the join. But in practice I have not been able to make it work. Here's a sample script:

set serveroutput on

create table t_dave1
(
mykey number
primary key,
myvalue varchar2(30)
);

create table t_dave2
(
mykey number
primary key,
myvalue varchar2(30)
);

insert into t_dave1 values (1, 'Before Update');

insert into t_dave2 values (1, 'Before Update');

commit;

declare
cursor mycur is
select t1.myvalue v1, t2.myvalue v2
from t_dave1 t1, t_dave2 t2
where t1.mykey = t2.mykey
for update of t1.myvalue, t2.myvalue;
begin
for myrec in mycur loop
dbms_output.put_line(myrec.v1||','||myrec.v2);
update t_dave1
set myvalue = 'After Update'
where current of mycur;
update t_dave2
set myvalue = 'After Update'
where current of mycur;
end loop;
for myrec in mycur loop
dbms_output.put_line(myrec.v1||','||myrec.v2);
end loop;
end;
/

The output from the PL/SQL block is:

Before Update,Before Update
Before Update,Before Update

whereas I was expecting:

Before Update,Before Update
After Update,After Update

Can you explain what I'm doing wrong?

Dave

and Tom said...

tkprof shows us "what went wrong". But before we go there, lets just understand what "update where current of" is doing.

Basically, when you use an "update where current of", PLSQL is retrieving the rowid at query time and issueing an update ... where rowid = :x for you.kkkkk consider:

create table t ( x int );
create table t2 ( x int );
insert into t values ( 1 );
insert into t2 values ( 1 );


declare
cursor c is select * from t for update;
l_rec t%rowtype;
begin
open c;
fetch c into l_rec;
update t set x = 2 where current of c;
close c;
end;
********************************************************************************
SELECT * FROM T FOR UPDATE

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 7 2 0
Fetch 1 0.00 0.00 0 7 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 3 0.00 0.00 0 14 2 1
********************************************************************************
UPDATE T SET X = 2 WHERE ROWID = :B1

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.01 0.00 0 0 0 0
Execute 1 0.00 0.00 0 1 1 1
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 0.01 0.00 0 1 1 1



so, all that did was to automate the fetching of a rowid and then an update by rowid..

When we do two tables, as long as only one of the tables is "for updatable", PLSQL does the right thing:



declare
cursor c is select t.x tx, t2.x t2x from t, t2 for update of t.x;
l_rec c%rowtype;
begin
open c;
fetch c into l_rec;
update t set x = 2 where current of c;
close c;
end;
********************************************************************************
SELECT T.X TX, T2.X T2X FROM T, T2 FOR UPDATE OF T.X

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 14 2 0
Fetch 1 0.00 0.00 0 14 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 3 0.00 0.00 0 28 2 1
********************************************************************************
UPDATE T SET X = 2 WHERE ROWID = :B1

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 1 1 1
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 0.00 0.00 0 1 1 1


it updated the single row in T, however, when it becomes ambigous as to which table would be modified:

declare
cursor c is select t.x tx, t2.x t2x from t, t2 for update of t.x, t2.x;
l_rec c%rowtype;
begin
open c;
fetch c into l_rec;
update t set x = 2 where current of c;
close c;
end;
********************************************************************************
SELECT T.X TX, T2.X T2X FROM T, T2 FOR UPDATE OF T.X, T2.X

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 14 3 0
Fetch 1 0.00 0.00 0 14 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 3 0.00 0.00 0 28 3 1
********************************************************************************
UPDATE T SET X = 2 WHERE ROWID = :B1

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 0.00 0.00 0 0 0 0


no rows updated -- the rowid isn't "known"

While you may reference multiple tables in a FOR UPDATE, only one may be in the for update clause if you wish to use where current of.

I personally have never used where current of, the technique I always use is as follows:

ops$tkyte@ORA9IR2> declare
2 cursor mycur is
3 select t1.myvalue v1, t1.rowid t1rid,
4 t2.myvalue v2, t2.rowid t2rid
5 from t_dave1 t1, t_dave2 t2
6 where t1.mykey = t2.mykey
7 for update of t1.myvalue, t2.myvalue;
8 begin
9 for myrec in mycur loop
10 dbms_output.put_line(myrec.v1||','||myrec.v2);
11 update t_dave1
12 set myvalue = 'After Update'
13 where rowid = myrec.t1rid;

14 update t_dave2
15 set myvalue = 'After Update'
16 where rowid = myrec.t2rid;

17 end loop;
18 for myrec in mycur loop
19 dbms_output.put_line(myrec.v1||','||myrec.v2);
20 end loop;
21 end;
22 /
Before Update,Before Update
After Update,After Update

PL/SQL procedure successfully completed.





Rating

  (2 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

thanks for this explanation

Terry, November 10, 2005 - 5:13 am UTC

I was wondering why it didn't work when referencing more than one table in the "for update of" clause.

this is the only site where i got an answer as often,

Thank for your time!


Nice one

Zoe, February 05, 2007 - 6:09 am UTC

Took a bit of trawling for this (as the search criteria seems to not want to put exact quotes together), but once found, great that I can keep my code from being repetitive.
I would have had to repeat the where clause 3 times for 3 different tables...

Thanks

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library