Skip to Main Content
  • Questions
  • Updating the columns in a table based on another table columns values.

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Rahul.

Asked: June 29, 2006 - 2:10 am UTC

Last updated: June 30, 2006 - 12:43 pm UTC

Version: 9.2.0

Viewed 1000+ times

You Asked

Hi Tom!

I've a big table "x" which has columns: id, month, quantity_ordered, quantity_received.

The table contains about 2,00,000 records.

There is another big table "y" which has the same number of records as "x", with the columns: id, month, quantity.

What I want to do is update table x, set quantity_ordered and quantity_received equal to quantity of table y where x.id = y.id and x.month = y.month.

The simple query I wrote is :

UPDATE x tab1
SET tab1.quantity_ordered = (
SELECT tab2.quantity
FROM y tab2
WHERE tab2.id = tab1.id
AND tab2.month = tab1.month
);

COMMIT;

The above query updates only the quantity_ordered column from "x". It does the required activity, but it takes lot of time, i.e., over 45 min. Also I didn't try updating the second column, i.e., quantity_received from "x", since updating 2,00,000 records for only one column takes lot of time.

Whereas, if I try to do the same thing by clubbing records of each month into a separate table, say y_01, y_02, etc., then things are better.

For eg.,

UPDATE x tab1
SET tab1.quantity_ordered = (
SELECT tab2.quantity
FROM y_01 tab2
WHERE tab2.id = tab1.id
AND tab2.month = tab1.month
)
WHERE tab1.month = '01';

COMMIT;

This above query does it quite fast, say about 5 min.

Can you please tell me as to how to update both quantity_ordered and quantity_received columns both in one shot while the update is really fast?

Thanks & Regards
-Rahul.


and Tom said...

Update a join....



ops$tkyte@ORA9IR2> create table x ( id number, month number, quantity_ordered number, quantity_received number );

Table created.

Elapsed: 00:00:00.02
ops$tkyte@ORA9IR2> insert /*+ APPEND */ into x
2 select id, mod(id,12)+1, object_id, data_object_id
3 from big_table.big_table;

1000000 rows created.

Elapsed: 00:00:02.28
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> create table y ( id number , month number, quantity_ordered number, quantity_received number, primary key(id,month) );

Table created.

Elapsed: 00:00:00.03
ops$tkyte@ORA9IR2> insert /*+ APPEND */ into y
2 select id, mod(id,12)+1, rownum, object_id
3 from big_table.big_table;

1000000 rows created.

Elapsed: 00:00:40.50
ops$tkyte@ORA9IR2> commit;

Commit complete.

Elapsed: 00:00:00.02
ops$tkyte@ORA9IR2> exec dbms_stats.gather_table_stats( user, 'X' );

PL/SQL procedure successfully completed.

Elapsed: 00:00:08.39
ops$tkyte@ORA9IR2> exec dbms_stats.gather_table_stats( user, 'Y', cascade=>true );

PL/SQL procedure successfully completed.

Elapsed: 00:00:15.78
ops$tkyte@ORA9IR2> set timing on
ops$tkyte@ORA9IR2> set autotrace on explain
ops$tkyte@ORA9IR2> update (select x.quantity_ordered xqo,
2 x.quantity_received xqr,
3 y.quantity_ordered yqo,
4 y.quantity_received yqr
5 from x, y
6 where x.id = y.id
7 and x.month = y.month )
8 set xqo = yqo, xqr = yqr;

1000000 rows updated.

Elapsed: 00:02:31.42

Execution Plan
----------------------------------------------------------
0
UPDATE STATEMENT Optimizer=CHOOSE (Cost=2282 Card=1000000 Bytes=30000000)


1 0
UPDATE OF 'X'


2 1
HASH JOIN (Cost=2282 Card=1000000 Bytes=30000000)


3 2
TABLE ACCESS (FULL) OF 'X' (Cost=386 Card=1000000 Bytes=13000000)


4 2
TABLE ACCESS (FULL) OF 'Y' (Cost=486 Card=1000000 Bytes=17000000)


And if a single index is actually used during the update - that is a problem, we want two full scans and a hash join!

Rating

  (4 ratings)

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

Comments

Update Join

A reader, June 29, 2006 - 5:26 pm UTC

Tom,
I faced some problem before for update join ...
One of the condition in update join is that the all columns of the table from which the updates are done, must be part primary key (table y in your example).
Regards,


Tom Kyte
June 29, 2006 - 6:52 pm UTC

That isn't a problem - it is a FACT.

If the join key ISN'T unique in the table you join to - then the update is not deterministic (means there would be more than one row X gets joined to in Y).

Hence the need for the primary key is not a problem - it must be true.

Okay it is fact!

A reader, June 30, 2006 - 9:17 am UTC

Tom,
Thanks. It means we can not use cool *** update join *** in such cases, thus reverting to old slow method.
Regards,
Tarun

Tom Kyte
June 30, 2006 - 12:37 pm UTC

in *what* cases?

As stated, the table you are joining to *must* be able to have a primary/unique constraint (if it cannot - the update of the join is useless, but not any more useless than trying to use the table to update the other in the first place)


You can use merge if you didn't have the key in place, but the fact is - the KEY MUST BE "ok" to have in place in the first place.

Thanks for the excellent examples!

Srinivas Narashimalu, June 30, 2006 - 10:28 am UTC

Hi Tom,

Thanks for the excellent examples! It helps us a great deal in understanding Oracle better.

I have a request, can you please store all the scripts that you use in some place with a link pointing to it so that whenever we want to reproduce the example and try different solutions we are able to do? I want to reproduce the big_table that you have in your example, can you tell me how you created it? I have tried to search the site but it's taking me to other links where you have used big_table.

Thanks,
Srinivas

Tom Kyte
June 30, 2006 - 12:43 pm UTC

create table big_table
as
select rownum id, a.*
from all_objects a
where 1=0
/
alter table big_table nologging;

declare
l_cnt number;
l_rows number := &1;
begin
insert /*+ append */
into big_table
select rownum, a.*
from all_objects a
where rownum <= &1;

l_cnt := sql%rowcount;

commit;

while (l_cnt < l_rows)
loop
insert /*+ APPEND */ into big_table
select rownum+l_cnt,
OWNER, OBJECT_NAME, SUBOBJECT_NAME,
OBJECT_ID, DATA_OBJECT_ID,
OBJECT_TYPE, CREATED, LAST_DDL_TIME,
TIMESTAMP, STATUS, TEMPORARY,
GENERATED, SECONDARY
from big_table
where rownum <= l_rows-l_cnt;
l_cnt := l_cnt + sql%rowcount;
commit;
end loop;
end;
/

alter table big_table add constraint
big_table_pk primary key(id)
/

begin
dbms_stats.gather_table_stats
( ownname => user,
tabname => 'BIG_TABLE',
method_opt => 'for all indexed columns',
cascade => TRUE );
end;
/
select count(*) from big_table;


Thanks very much!

Srinivas Narashimalu, June 30, 2006 - 2:01 pm UTC

Thanks you!

Srinivas

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