Oracle FAQ | Your Portal to the Oracle Knowledge Grid |
Home -> Community -> Usenet -> c.d.o.misc -> Re: How do I bind variables to dynamic SQL?
On Mon, 6 Sep 1999 11:52:00 +0100, you wrote:
>
>have a look at my web-site; one item describes
>binding variables in PL/SQL blocks using dbms_sql,
>another item (for users of oracle 8) describes using
>object types to pass a list of values as a single parameter.
>
>You SQL then does:
> where colX in (cast varray bind variable into subquery)
>or
> where colX not in (cast varray bind variable into subquery)
>
>
>--
>
>Jonathan Lewis
>Yet another Oracle-related web site: http://www.jlcomp.demon.co.uk
>
>dperez_at_juno_nospam.com wrote in message
><37cf22b7.3496227_at_news.uswest.net>...
>>Um, Thomas, I think you may have missed the most important point in my
>original
>>post:
>>
>> The string of comma-delimited integers MUST remain intact.
>> Parsing it, and creating a varying number of
>> predicates would NOT solve the problem.
>>
Yes it will. As I said (my most important point in my post)
The only way you can do this and support bind variables is to PARSE it (but not create a varying number of predicates) and set some upper limit on the total number of IN list values (which is OK, since we have a hard limit anyway:
If you want bind variables -- you will parse, no ifs ands ors about it -- you will parse -- the integer list MUST be broken out into its individual entries -- it has to be.
Since you never specified "not in" in the original request -- yes, we have to rework this a little. But the fact is -- it can easily be done.
X NOT IN ( a, b, c )
is ALMOST the same as:
NOT EXISTS ( select null from dual where X in ( a, b, c ) )
This difference between the two is that if any of A, B or C is NULL the first predicate never returns TRUE or FALSE (and thats your problem with the original solution) but the second one does (it lets A, B, or C be null and still returns records -- which is the desired effect)
So, we can use a postive IN list (which allows for the extra nulls) and negate it. A modified version of a solution is thus:
tkyte_at_8.0> create or replace procedure dynquery( p_in_list in varchar2,
2 p_use_not in boolean default FALSE ) 3 is 4 l_tmp_list varchar2(4096) default p_in_list || ','; 5 l_n number; 6 6 l_theCursor integer default dbms_sql.open_cursor; 7 l_columnValue varchar2(2000); 8 l_status integer; 9 l_query varchar2(1000) 10 default 'select empno, ename 11 from emp 12 where '; 13 l_descTbl dbms_sql.desc_tab; 14 l_colCnt number; 15 l_sep char(1) default ' '; 16 16 MAX_COLS number default 7; 17 begin 18 if ( p_use_not ) 19 then 20 l_query := l_query || ' not exists ( select null from dual where EMPNO in ('; 21 else 22 l_query := l_query || 'empno in ('; 23 end if; 24 24 for i in 1 .. MAX_COLS loop 25 l_query := l_query || l_sep || ':i' || i; 26 l_sep := ','; 27 end loop; 28 if ( p_use_not ) then 29 l_query := l_query || '))'; 30 else 31 l_query := l_query || ')'; 32 end if; 33 33 dbms_output.put_line( l_query ); 34 dbms_sql.parse( l_theCursor, l_query, dbms_sql.native ); 35 35 for i in 1 .. MAX_COLS loop 36 l_n := instr( l_tmp_list, ',' ); 37 -- this ends up binding NULL eventually after 38 -- we exhaust the in list 39 39 dbms_sql.bind_variable( l_theCursor, ':i' || i, 40 to_number(substr( l_tmp_list, 1, l_n-1 )) ); 41 l_tmp_list := substr( l_tmp_list, l_n+1 ); 42 end loop; 43 43 dbms_sql.describe_columns( l_theCursor, l_colCnt, l_descTbl ); 44 44 for i in 1 .. l_colCnt loop 45 dbms_sql.define_column( l_theCursor, i, l_columnValue, 2000 ); 46 end loop; 47 47 l_status := dbms_sql.execute(l_theCursor); 48 48 while ( dbms_sql.fetch_rows(l_theCursor) > 0 ) loop 49 dbms_sql.column_value( l_theCursor, 1, l_columnValue ); 50 dbms_output.put_line( l_columnValue ); 51 dbms_sql.column_value( l_theCursor, 2, l_columnValue ); 52 dbms_output.put_line( l_columnValue ); 53 end loop;
Procedure created.
tkyte_at_8.0>
tkyte_at_8.0> exec dynquery( '7566, 7654, 7698, 7782, 7788' )
select empno, ename
from emp where empnoin ( :i1,:i2,:i3,:i4,:i5,:i6,:i7)
tkyte_at_8.0> exec dynquery( '7566, 7654, 7698, 7782, 7788', TRUE ) select empno, ename
from emp where notexists ( select null from dual where EMPNO in ( :i1,:i2,:i3,:i4,:i5,:i6,:i7)) 7369
If you use the trick at Jonathan's web site, that'll work as well (but again, you'll be parsing that list of numbers).
>>
>>Parsing the values and retrieiving them one at a time works fine... Been
>there,
>>done that.......... BUT, in the actual program there are ACTUALLY TWO
>>QUERIES...........
>>
>>1: select *... WHERE ... IN (set...)
>>
>>2: select *... WHERE ... NOT IN (set...)
>>
>>Parsing the string into some unknown series of individual values then
>putting
>>each in the select statement would seem not to work well when doing a "not
>>in"....... Thus, the reason for the original statement "THE STRING OF
>>COMMA-DELIMITED INTEGERS MUST REMAIN INTACT."
>>
if we get the question -- not a direction to give a particular (impossible) solution we'll be able to answer them much better.
Your requirement:
NO PARSING plus the desired feature:
USE BIND VARIABLES is simply not possible to achieve. If you had asked instead "given a comma separated list of numbers -- i would like to have a query i could use to let me to an IN using bind variables on that list as well as a query that lets me do NOT IN on that list" -- we could have given you the solutions much faster.
>>I'll give the instr a shot. It may have such serious performance
>implications
>>its not usable, but its worth a shot.
>>
beware the instr:
tkyte_at_8.0> variable object_list varchar2(25) tkyte_at_8.0> tkyte_at_8.0> exec :object_list := '10, 11, 12, 13, 14, 15'
PL/SQL procedure successfully completed.
tkyte_at_8.0>
tkyte_at_8.0> select user_id
2 from all_users a where
3 instr( :object_list|| ',', to_char( a.user_id) || ',') <> 0
4 /
USER_ID
0 5
(neither 5 nor 0 was in the in list)
it's tricky to use the instr (gotta send in a string with delimiters around each element else you'll get the wrong answer as above). Its not very efficient if the thing being searched on can be used in the index.
I'd go with the <not> in list for performance and meaning...
>
--
See http://govt.us.oracle.com/~tkyte/ for my columns 'Digging-in to Oracle8i'...
Current article is "Part I of V, Autonomous Transactions" updated June 21'st
Thomas Kyte tkyte_at_us.oracle.com Oracle Service Industries Reston, VA USA
Opinions are mine and do not necessarily reflect those of Oracle Corporation Received on Mon Sep 06 1999 - 12:52:35 CDT