With a little more code than the previous solution we can write a pure SQL query that implements a varying IN list without supressing indexes. The key is to use logic that converts a string to a set of rows. Fortunately we have a number of these techniques in our arsenal at SQL Techniques Tutorials: String to Rows. In the next example we employ a method described at String to Rows: Integer Series Generator Method.
variable d varchar2 execute :d := ',' variable p varchar2(100) execute :p := '1,3,5' select * from t where key in ( select to_number ( substr ( p, instr( p, :d, 1, i.position ) + 1, instr( p, :d, 1, i.position + 1 ) - ( instr( p, :d, 1, i.position ) + 1 ) ) ) as val from ( select column_value as position , :d || :p || :d as p from table( integer_varray_type( 1,2,3,4,5,6,7,8,9,10 ) ) where column_value <= length( :p || :d ) - nvl( length( replace( :p, :d, null ) ), 0 ) ) i ) order by 1 ;
KEY C ---------- ---------- 1 v1 3 v3 5 v5
Duplicates in the IN list produce the same result as if there were no duplicates.
execute :p := '1,3,3,3,3,5' /
KEY C ---------- ---------- 1 v1 3 v3 5 v5
The query accepts IN lists with spaces.
execute :p := '1, 2, 3, 4, 5' /
KEY C ---------- ---------- 1 v1 2 v2 3 v3 4 v4 5 v5
If the IN list is null no rows are returned.
execute :p := null PL/SQL procedure successfully completed. / no rows selected
For quoted string lists like 'v1', 'v3', 'v5' we need to tweak the logic slightly.
-- the delimiter is a single quote this time execute :d := '''' execute :p := q'['v1', 'v3', 'v5']' select * from t WHERE C IN ( select substr ( p, instr( p, :d, 1, i.position ) + 1, instr( p, :d, 1, i.position + 1 ) - ( instr( p, :d, 1, i.position ) + 1 ) ) as val from ( select column_value as position , :P AS P from table( INTEGER_VARRAY_TYPE( 1,3,5,7,9,11,13,15,17,19 ) ) where column_value < NVL(LENGTH(:P),1) - NVL( LENGTH( REPLACE( :P, :D, NULL ) ), 0 ) ) i ) order by 1 ;
KEY C ---------- ---------- 1 v1 3 v3 5 v5