Skip to Main Content
  • Questions
  • Will defining variables be faster than reparsing an update statement?

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Terry.

Asked: December 09, 2019 - 5:24 am UTC

Last updated: December 10, 2019 - 10:35 am UTC

Version: 11.2.0.4

Viewed 1000+ times

You Asked

Good day "Toms",

I am working on a project which performs bulk updates of millions of rows. Unfortunately each update is currently a separate update statement which updates 2 columns with unique values and uses a where clause with a unique ID to identify the row to be updated.

Every update will be reparsed because the values are literals.
If I define them as variables before each update, will this actually save time, or is there a better way? I am stumped. VALUE1, VALUE2 and VALUE3 are all supplied, not from inside the database.

The statement is:
UPDATE TABLE1 SET COL1 = 'VALUE1',COL2 = 'VALUE2' WHERE COL3 = 'VALUE3';


I am looking at:
define var1='VALUE1'
define var2='VALUE2'
define var3='VALUE3'
UPDATE TABLE1 SET COL1 = '&var1',COL2 = '&var2' WHERE COL3 = '&var3';


But I would have to redefine the vars for each update and feel this will negate the gain of having the update statement cached.
I was really hoping to use FORALL, but can't make it work with all external supplied values. Any suggestions?

and Chris said...

But I would have to redefine the vars for each update and feel this will negate the gain of having the update statement cached.

It looks like you're using SQL*Plus substitution variables. In which case the net effect is the same as running the update with the values hard-coded. i.e. SLOW!

To improve this you need to use bind variables.

I'm not clear where the values are coming from or in which format. The example below shows you how to build an array containing the IDs you want to update. And forall to use these in a single update change the rows.

V2 & V3 have one value here. But if you need to assign a different value for each row, you could also initialize these in an array.

create table t (
  c1 int, c2 int, c3 int
);

insert into t 
  select level, level, level 
  from   dual
  connect by level <= 5;

commit;

select * from t;

C1     C2     C3   
    1     1     1 
    2     2     2 
    3     3     3 
    4     4     4 
    5     5     5 

declare
  ids dbms_sql.number_table;
  v2  int := 0;
  v3  int := 99;
begin
  for i in 1 .. 3 loop
    ids ( i ) := i;
  end loop;
  
  forall rws in 1 .. ids.last
    update t
    set    c2 = v2, c3 = v3
    where  c1 = ids ( rws );

end;
/

select * from t;

C1     C2     C3   
    1     0    99 
    2     0    99 
    3     0    99 
    4     4     4 
    5     5     5 

Rating

  (1 rating)

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

Comments

Not quite what I am looking at...

Terry Jones, December 09, 2019 - 10:18 pm UTC

Thank you for your time Chris,
I am not sure that this will help me.
I am currently looking at multiple files with half a million individual update statements, each statement with the values 1 to 3, hard coded in them and all 3 of them different for each update.
Having the values externally supplied, and then trying to avoid going back and forth between the SQL and PL/SQL engines is what is perplexing me.
The more PL/SQL architecture I have to build, I feel the less likely it is to provide any gain.
What do you think?
Chris Saxon
December 10, 2019 - 10:35 am UTC

Half a million update statements?! Yikes.

To be honest, I don't understand what you're trying to do. Other than "update some rows".

Where exactly do the hardcoded values come from? How do they get from the client to your code? What exactly does your code look like now?

The more PL/SQL architecture I have to build, I feel the less likely it is to provide any gain.

This is unlikely. If you can replace the 500,000+ updates with a single FORALL update you'll get substantial performance gains.

For example, you can create arrays for the new values and set them for each like so:

declare
  ids dbms_sql.number_table;
  v2s dbms_sql.number_table;
  v3s dbms_sql.number_table;
begin
  for i in 1 .. <half a million?!> loop
    ids ( i ) := i;
    v2s ( i ) := <value>;
    v3s ( i ) := <value>;
  end loop;
  
  forall rws in 1 .. ids.last
    update t
    set    c2 = v2s ( rws ), c3 = = v2s ( rws )
    where  c1 = ids ( rws );

end;
/


Of course, this assumes you can't replace all the separate updates with just one that updates thousands of rows. This is likely to be fastest of all.

More to Explore

SQL

The Oracle documentation contains a complete SQL reference.