Oracle FAQ Your Portal to the Oracle Knowledge Grid
HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US
 

Home -> Community -> Usenet -> c.d.o.tools -> Re: Need help passing parameter into Stored Proc

Re: Need help passing parameter into Stored Proc

From: Thomas J. Kyte <tkyte_at_us.oracle.com>
Date: 2000/06/27
Message-ID: <8jbers$6of$1@nnrp1.deja.com>

In article <8jb12d$7vh$1_at_nnrp2.deja.com>,   vladn2001_at_my-deja.com wrote:
> Hi everybody,
>
> I have a stored procedure that needs to update
> multiple products in one call based on item_sku
> that I am passing as a parameter.
>
> When item sku are hardcoded it executes fine:
>
> UPDATE PRODUCTS SET DESCR = '123TEST' WHERE
> ITEM_SKU IN ('371948,371872')
>
> However, when I am passing those items skus as a
> variable this statement will not execute at all:
>
> vITEM_SKU IN VARCHAR2(255);
>
> --Hardcoded for testing
> vITEM_SKU := '371948,371872';
>
> UPDATE PRODUCTS SET DESCR = '123TEST' WHERE
> ITEM_SKU IN (vITEM_SKU);
>
> I assume that Oracle handles this variable as one
> string, therefore treating it as one item_sku.
> Is my assumption correct ? If it is, what can be
> done to work around this issue.
>
> I would appreciate any help or advice.
>
> Thanks, Davie (eclipse98_at_hotmail.com)
>
> Sent via Deja.com http://www.deja.com/
> Before you buy.
>

sigh... never a version number to be found...

You want to do this one of the following ways -- do NOT use dynamic sql and concatenate in the values as constants, that will only trash your shared pool. The following are "bind variable" friendly.

In Oracle7, this is rather difficult. You cannot bind in a SINGLE value and expect it to be treated as MANY values (think about how hard it would be to find something in an IN LIST that had a "comma" in it for example).

In Oracle8.0 and up, this is easy with object types and nested tables. Just to see what that will look like, I'll show that - then I'll show the v7 implementation.

ops$tkyte_at_8i> create or replace type myTableType

              as table of number;
  2 /
Type created.

ops$tkyte_at_8i> create or replace function in_list

                     ( p_string in varchar2 ) return myTableType
  2  as
  3      l_data             myTableType := myTableType();
  4      l_string           long default p_string || ',';
  5      l_n                number;

  6 begin
  7
  8 loop
  9      exit when l_string is null;
 10      l_data.extend;
 11      l_n := instr( l_string, ',' );
 12      l_data( l_data.count ) := substr( l_string, 1, l_n-1 );
 13      l_string := substr( l_string, l_n+1 );
 14 end loop;
 15 return l_data;
 16 end;
 17 /

Function created.

So, in Oracle8 and up we can "select * from PLSQL_FUNCTION" when the function returns a SQL Table type as follows:

ops$tkyte_at_8i>
ops$tkyte_at_8i> select *
  2 from THE ( select cast( in_list('1,2,3,5,12')

                        as mytableType ) from dual ) a
  3 /

COLUMN_VALUE


           1
           2
           3
           5
          12

Which means we can do that in a subquery:

ops$tkyte_at_8i> select *
  2 from all_users
  3 where user_id in

  4      ( select * from THE (
               select cast( in_list('1,2,3,5,12')
                      as mytableType ) from dual ) a )
  5 /
USERNAME                          USER_ID CREATED
------------------------------ ---------- ---------
SYSTEM                                  5 20-APR-99




In v7, we can do something very similar (works in 8.0 and up as well) with plsql tables. Its a little more work but not too much. It would look like this:

ops$tkyte_at_8i> create or replace package my_pkg   2 as

  3          function in_list( p_string in varchar2,
                            p_idx in number ) return varchar2;
  4          pragma restrict_references( in_list,
                        wnds, rnds, wnps, rnps );
  5
  6          function get_list_count(p_string in varchar2)
                      return number;
  7          pragma restrict_references( get_list_count,
                            wnds, rnds, wnps, rnps );
  8
  9          pragma restrict_references( my_pkg,
                            wnds, rnds, wnps, rnps );
 10 end;
 11 /

Package created.

ops$tkyte_at_8i>
ops$tkyte_at_8i> create or replace package body my_pkg   2 as
  3 function in_list( p_string in varchar2,

                       p_idx in number ) return varchar2
  4  is
  5          l_start number;
  6          l_stop  number;

  7 begin
  8 if ( p_idx = 1 ) then
  9 l_start := 1;
 10 else
 11 l_start := instr( p_string, ',', 1, p_idx-1 )+1;  12 end if;
 13
 14 l_stop := instr( p_string ||',', ',', 1, p_idx );  15
 16 return ltrim( rtrim( substr( p_string, l_start,
                                       l_stop-l_start ) ) );
 17 end;
 18
 19 function get_list_count( p_string in varchar2 )

             return number
 20 is
 21 l_cnt number default 0;
 22 begin

 23     for i in 1 .. 1000 loop
 24        exit when nvl( instr( p_string, ',', 1, i ), 0 ) = 0;
 25        l_cnt := i+1;
 26     end loop;
 27
 28     return l_cnt;

 29 end;
 30
 31 end;
 32 /

Package body created.

So, now I can code a query like:

ops$tkyte_at_8i> variable bindVar varchar2(255)
ops$tkyte_at_8i>
ops$tkyte_at_8i> exec :bindVar := '1, 3, 44, 1000'

PL/SQL procedure successfully completed.

ops$tkyte_at_8i>
ops$tkyte_at_8i> select my_pkg.in_list( :bindVar, rownum )   2 from all_objects
  3 where rownum <=

         ( select my_pkg.get_list_count( :bindVar ) from dual )   4 /

MY_PKG.IN_LIST(:BINDVAR,ROWNUM)



1
3
44
1000

All we need for that to work is a table with MORE rows then we have IN LIST items -- all_objects is generally a good candidate.  It works by using rownum as an "index" into the plsql table.

So, now we can simply:

ops$tkyte_at_8i> select *
  2 from all_users
  3 where user_id in (select my_pkg.in_list(:bindVar,rownum)

  4                       from all_objects
  5                      where rownum <=
          ( select my_pkg.get_list_count( :bindVar ) from dual )
  6                                    )
  7 /
USERNAME                          USER_ID CREATED
------------------------------ ---------- ---------
WEB$AZEILMAN                         1000 24-JUN-99



--
Thomas Kyte (tkyte_at_us.oracle.com) Oracle Service Industries
Howtos and such: http://osi.oracle.com/~tkyte/index.html
Oracle Magazine: http://www.oracle.com/oramag
Opinions are mine and do not necessarily reflect those of Oracle Corp


Sent via Deja.com http://www.deja.com/
Before you buy.
Received on Tue Jun 27 2000 - 00:00:00 CDT

Original text of this message

HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US