Home » SQL & PL/SQL » SQL & PL/SQL » PL/SQL:How to use VARRAY in the USING clause of EXECUTE IMMEDIATE? (Oracle 10.2.0.5, Solaris 10 x86-64)
PL/SQL:How to use VARRAY in the USING clause of EXECUTE IMMEDIATE? [message #476463] Thu, 23 September 2010 08:27 Go to next message
sojo
Messages: 7
Registered: September 2006
Junior Member
Hi,

I'm trying to optimize an application running heavy updates/inserts/deletes, by having it using bind variables instead of "string queries".

The columns to be updated can vary (possibly from one column to all columns of a table), thus I have made som logic to build the query accordingly. My problem is now that I cant get the EXECUTE below to handle the VARRAY passed in the USING clause, it fails with "PLS-00457: expressions have to be of SQL types"


Environment:

create table table_x (a varchar2(10),b varchar2(10),c varchar2(10),d varchar2(10));

insert into table_x values('a','b','c','d');
commit;


Code, simplified with static number of columns:

declare
type v is varray(10) of varchar2(20);
v_values v:=v('A','B','c');
myupdate varchar2(2000);
begin
myupdate:='update table_x set a=:a, b=:b where c=:c';
dbms_output.put_line(myupdate);
execute immediate myupdate using v_values;
end;


I've understood that I cannot send TABLE type variables by USING, but this should be an VARRAY. Does anyone have a clue what I am missing/doing wrong?
Re: PL/SQL:How to use VARRAY in the USING clause of EXECUTE IMMEDIATE? [message #476465 is a reply to message #476463] Thu, 23 September 2010 08:29 Go to previous messageGo to next message
BlackSwan
Messages: 26766
Registered: January 2009
Location: SoCal
Senior Member
>Does anyone have a clue what I am missing/doing wrong?
You are mixing PL/SQL & SQL.
SQL is different from PL/SQL.
SQL can only utilize SQL datatypes; not PL/SQL datatypes.
Re: PL/SQL:How to use VARRAY in the USING clause of EXECUTE IMMEDIATE? [message #476487 is a reply to message #476465] Thu, 23 September 2010 10:16 Go to previous messageGo to next message
Michel Cadot
Messages: 68767
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator
Read the last article from T. Kyte in Oracle Magazine:
http://www.oracle.com/technetwork/issue-archive/2010/10-sep/o50asktom-165477.html

Regards
Michel
Re: PL/SQL:How to use VARRAY in the USING clause of EXECUTE IMMEDIATE? [message #476537 is a reply to message #476463] Thu, 23 September 2010 14:53 Go to previous messageGo to next message
Solomon Yakobson
Messages: 3305
Registered: January 2010
Location: Connecticut, USA
Senior Member
sojo wrote on Thu, 23 September 2010 09:27
I've understood that I cannot send TABLE type variables by USING, but this should be an VARRAY. Does anyone have a clue what I am missing/doing wrong?


You can't use PL/SQL declared types. Use something like:

SQL> select  *
  2    from  table_x
  3  /

A          B          C          D
---------- ---------- ---------- ----------
a          b          c          d

SQL> declare 
  2      v_values sys.OdciVarchar2List := sys.OdciVarchar2List('A','B','c');
  3      myupdate varchar2(2000);
  4  begin
  5      myupdate := 'declare
  6                       v_values sys.OdciVarchar2List;
  7                   begin
  8                       v_values := :1;
  9                       update table_x
 10                         set a=v_values(1),
 11                             b=v_values(2)
 12                         where c=v_values(3);
 13                   end;';
 14      dbms_output.put_line(myupdate);
 15      execute immediate myupdate using v_values;
 16  end;
 17  /

PL/SQL procedure successfully completed.

SQL> select  *
  2    from  table_x
  3  /

A          B          C          D
---------- ---------- ---------- ----------
A          B          c          d


SY.

[Updated on: Tue, 28 September 2010 23:30] by Moderator

Report message to a moderator

Re: PL/SQL:How to use VARRAY in the USING clause of EXECUTE IMMEDIATE? [message #477075 is a reply to message #476537] Tue, 28 September 2010 05:52 Go to previous messageGo to next message
sojo
Messages: 7
Registered: September 2006
Junior Member
Thanks SY, but not quite what I am after. I would like the query in line 9-12 to use bind variable, thus reading something like:
 9                       update table_x
 10                         set a=:a,
 11                             b=:b
 12                         where c=:c;
...
 15             execute immediate myupdate using v_values;


My current solution (which works, but will be a pain to maintain when the numbers of columns that might be updated is around 50 and might increase) looks like this:

--i=number of values in v

if i=1 then execute immediate myupdate using v_values(1);end if;
if i=2 then execute immediate myupdate using v_values(1),v_values(2);end if;
if i=3 then execute immediate myupdate using v_values(1),v_values(2),v_values(3);end if;
--and so on, and so on...


Is it possible to achive what I am asking, to populate the bind variables in the query by only using one object in the USING clause (and thereby remove the need for my 50 if-thens above)?

Also, another detail have shown up, one table is having a CLOB column which I will have to investigate further how to be able to cope whith that datatype as well...

[Updated on: Tue, 28 September 2010 07:26]

Report message to a moderator

Re: PL/SQL:How to use VARRAY in the USING clause of EXECUTE IMMEDIATE? [message #477121 is a reply to message #477075] Tue, 28 September 2010 16:08 Go to previous messageGo to next message
Barbara Boehmer
Messages: 9106
Registered: November 2002
Location: California, USA
Senior Member
I believe that Solomon gave you what you asked for, but you are just not recognizing it. If your objective is to use bind variables in order to save hard parsing to increase efficiency, then I believe it does that. The bind variable :1 is in this line:

v_values := :1;


The myupdate generated consists of an entire pl/sql block from declare to end. The dbms_output of that block does not show any hard-coded values:

declare
  v_values sys.OdciVarchar2List;
begin
  v_values := :1;
  update table_x
  set    a=v_values(1),
         b=v_values(2)
  where  c=v_values(3);
end;


The dynamic execution only uses one object:

execute immediate myupdate using v_values;


It may be slightly confusing that v_values was used in the generated block and the outer block. In the following example, I have replaced v_values with v_values1 in the outer block and v_values with v_values2 in the inner block in an attempt to clarify that the generated block in the update statement is using the bound values in values2 and the using clause is applying the values from values1 in the outer static block.

SCOTT@orcl_11gR2> select * from table_x
  2  /

A          B          C          D
---------- ---------- ---------- ----------
a          b          c          d

1 row selected.

SCOTT@orcl_11gR2> declare
  2  	 v_values1 sys.OdciVarchar2List := sys.OdciVarchar2List('A','B','c');
  3  	 myupdate  varchar2(2000);
  4  begin
  5  	 myupdate := 'declare
  6  			  v_values2 sys.OdciVarchar2List;
  7  		      begin
  8  			  v_values2 := :1;
  9  			  update table_x
 10  			     set a=v_values2(1),
 11  				 b=v_values2(2)
 12  			   where c=v_values2(3);
 13  		      end;';
 14  	 dbms_output.put_line(myupdate);
 15  	 execute immediate myupdate using v_values1;
 16  end;
 17  /

PL/SQL procedure successfully completed.

SCOTT@orcl_11gR2> select * from table_x
  2  /

A          B          C          D
---------- ---------- ---------- ----------
A          B          c          d

1 row selected.

SCOTT@orcl_11gR2>

[Updated on: Tue, 28 September 2010 23:28] by Moderator

Report message to a moderator

Re: PL/SQL:How to use VARRAY in the USING clause of EXECUTE IMMEDIATE? [message #477151 is a reply to message #477121] Wed, 29 September 2010 01:22 Go to previous messageGo to next message
sojo
Messages: 7
Registered: September 2006
Junior Member
Rookie as I am, please be patient with my very limited (so far, but hopefully increasing) knowledge... =)

I now (think I) realize what is happening here, but for the sake of clarity I will try to explain it, and please feel free to confirm/deny the correctness of it.

The complete "declare...begin...end" clause is parsed/compiled (and cached), when called by the execute immediate, and therefore it is possible to send the OdciVarchar2List into the code, rather than to the specific query part of the code.

My mistake was that I thought that only the query (within the total code) was the part being parsed/cached, and for some reason I did not "see" that the query anyway was written "statically"...

SY, my apologies, and thank you again, I will look further into this solution.
Barbara, thank you for making me read this again, and really think/understand.


As I also mentioned in the beginning, I have some logic for building up the query to handle an unknown number of columns, so I will still be parsing a high amount (currently any combination and number of a total of 55 columns) of queries through this code, but I guess it will still be better then having to parse each and every update call, as is the current situation.
Re: PL/SQL:How to use VARRAY in the USING clause of EXECUTE IMMEDIATE? [message #477154 is a reply to message #477151] Wed, 29 September 2010 01:33 Go to previous messageGo to next message
Michel Cadot
Messages: 68767
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator
On variable number of columns in update read the latest T. Kyte's article on Oracle Magazine:
http://www.oracle.com/technetwork/issue-archive/2010/10-sep/o50asktom-165477.html

Regards
Michel

[Updated on: Wed, 29 September 2010 01:34]

Report message to a moderator

Re: PL/SQL:How to use VARRAY in the USING clause of EXECUTE IMMEDIATE? [message #477246 is a reply to message #477154] Wed, 29 September 2010 12:50 Go to previous messageGo to next message
Barbara Boehmer
Messages: 9106
Registered: November 2002
Location: California, USA
Senior Member
The following example combines the method provided by Solomon with the concepts in the article by Tom Kyte that Michel posted twice. I have separated the values into two sets of variables for binding, those that will be used in the set clause, and those that will be used in the where clause. The idea is that you create one update statement using all 55 columns with nvl applied to each column. Then you pass however many variables you need to with null values for the missing values. This way no matter how many columns you are updating or how many where conditions you have, or how many different values you use, you are still running the same update statement without re-parsing. I have demonstrated with only 4 columns, but indicated where you would need to increase to 55 columns.

SCOTT@orcl_11gR2> select * from table_x
  2  /

A          B          C          D
---------- ---------- ---------- ----------
a          b          c          d

1 row selected.

SCOTT@orcl_11gR2> declare
  2  	 v_set1    sys.OdciVarchar2List :=
  3  		     sys.OdciVarchar2List
  4  		       ('A','B');
  5  	 v_where1  sys.OdciVarchar2List :=
  6  		     sys.OdciVarchar2List
  7  		       (null, null, 'c');
  8  	 myupdate  varchar2(2000);
  9  begin
 10  	 while v_set1.count < 55 loop
 11  	   v_set1.extend;
 12  	   v_set1(v_set1.count) := null;
 13  	 end loop;
 14  	 while v_where1.count < 55 loop
 15  	   v_where1.extend;
 16  	   v_where1(v_where1.count) := null;
 17  	 end loop;
 18  	 myupdate := 'declare
 19  			  v_set2    sys.OdciVarchar2List;
 20  			  v_where2  sys.OdciVarchar2List;
 21  		      begin
 22  			  v_set2   := :1;
 23  			  v_where2 := :2;
 24  			  update table_x
 25  			     set a = nvl (v_set2(1), a),
 26  				 b = nvl (v_set2(2), b),
 27  				 c = nvl (v_set2(3), c),
 28  				 d = nvl (v_set2(4), d)
 29  				 -- and so on for all 55 columns
 30  			   where a = nvl (v_where2(1), a)
 31  			   and	 b = nvl (v_where2(2), b)
 32  			   and	 c = nvl (v_where2(3), c)
 33  			   and	 d = nvl (v_where2(4), d);
 34  			   -- and so on for all 55 columns
 35  		      end;';
 36  	 -- dbms_output.put_line(myupdate);
 37  	 execute immediate myupdate using v_set1, v_where1;
 38  end;
 39  /

PL/SQL procedure successfully completed.

SCOTT@orcl_11gR2> select * from table_x
  2  /

A          B          C          D
---------- ---------- ---------- ----------
A          B          c          d

1 row selected.

SCOTT@orcl_11gR2>

[Updated on: Wed, 29 September 2010 13:06]

Report message to a moderator

Re: PL/SQL:How to use VARRAY in the USING clause of EXECUTE IMMEDIATE? [message #477248 is a reply to message #477246] Wed, 29 September 2010 13:19 Go to previous messageGo to next message
Michel Cadot
Messages: 68767
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator
Quote:
in the article by Tom Kyte that Michel posted twice

Ha ha ha, didn't realize that I already posted it. I'm quite constant in my advices... Smile

Regards
Michel

[Updated on: Wed, 29 September 2010 13:49]

Report message to a moderator

Re: PL/SQL:How to use VARRAY in the USING clause of EXECUTE IMMEDIATE? [message #477317 is a reply to message #477248] Thu, 30 September 2010 02:59 Go to previous messageGo to next message
sojo
Messages: 7
Registered: September 2006
Junior Member
Michel Cadot wrote on Wed, 29 September 2010 21:19

Ha ha ha, didn't realize that I already posted it. I'm quite constant in my advices... Smile

Regards
Michel

Rather that than the opposite... =)

I read the article, and although I get the basics, I do not feel it will meet my needs this time:

-if the application is sending an "update table set column=null", I will not get the correct update, but rather the old value
-although he writes that redo/undo impact is small if indexes are not updated, in my case I (currently) do not have the columns indexed, but rather PK:ed (which will not change), so to my setup, this looks like "unnecessary column updates" causing redo (which is the other thing I am looking to minimize)

Re: PL/SQL:How to use VARRAY in the USING clause of EXECUTE IMMEDIATE? [message #477323 is a reply to message #477317] Thu, 30 September 2010 03:42 Go to previous message
Michel Cadot
Messages: 68767
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator
Quote:
-if the application is sending an "update table set column=null", I will not get the correct update, but rather the old value

Yes this is the behaviour the article wants, if yours is different then change the condition test.

Quote:
this looks like "unnecessary column updates" causing redo (which is the other thing I am looking to minimize)

This is the cost to pay to have this variability flexibility with simple code. What do you expect?

Regards
Michel
Previous Topic: PK-FK Joins
Next Topic: concatenate a parameter with a column in a sql query
Goto Forum:
  


Current Time: Mon Aug 25 23:04:23 CDT 2025