我的包主体之一中包含以下动态SQL
OPEN ccur for 'select c.category from test_category c where c.deptid='||PI_N_Dept || ' and c.category not in ('|| sExcludeCategories ||')';
sExcludeCategories将包含一组用逗号分隔的整数。我想消除此动态SQL语句。有什么聪明的方法可以做到这一点吗?
sExcludeCategories
我猜你知道你可以绑定一个变量PI_N_Dept来删除那条动态sql。不幸的是,对于您的IN子句,sExcludeCategories您无法在Oracle中为列表绑定变量(据我所知至少为9.2)
PI_N_Dept
IN
您确实有几种选择。您当前的解决方案是最简单的。另一个解决方案是将过程更改为接受多个变量并创建AND语句列表。
'select c.category from test_category c where c.deptid= :PI_N_Dept and c.category <> :sExcludeCategory1 and c.category <> :sExcludeCategory2 and c.category <> :sExcludeCategory3 ';
或具有固定的IN值列表
'select c.category from test_category c where c.deptid= :PI_N_Dept and c.category not in (:sExcludeCategory1 , :sExcludeCategory2, :sExcludeCategory3)';
在只需要2个类别的情况下,您必须要小心。第三个必须设置为不在c.category中的某个值(注意:请注意,在此处测试null值)
Ask Tom中提供了另一个解决方案。这看起来很简单,尽管我还没有测试过。它通过创建一个函数str2tbl()来工作,该函数允许您传递一系列用逗号分隔的数字,并通过对偶创建一个“表”来进行IN。
create or replace type myTableType as table of number; create or replace function str2tbl( p_str in varchar2 ) return myTableType as l_str long default p_str || ','; l_n number; l_data myTableType := myTabletype(); begin loop l_n := instr( l_str, ',' ); exit when (nvl(l_n,0) = 0); l_data.extend; l_data( l_data.count ) := ltrim(rtrim(substr(l_str,1,l_n-1))); l_str := substr( l_str, l_n+1 ); end loop; return l_data; end;
您的示例看起来像
'select c.category from test_category c where c.deptid= :PI_N_Dept and c.category not in ( select * from INLIST ( select cast( str2tbl( :sExcludeCategories ) as mytableType ) from dual ) )';
仅当sExcludeCategories有数字列表时,此方法才有效。如果引号包含在变量中(并且您无法更改),则必须更改str2tbl以处理引号,并更改myTableTypeto的类型varchar2(10)或更合适的名称。
myTableType
varchar2(10)
总体而言,如果原始sql不影响性能,那么为了简单起见,我将其保留为动态SQL。保持头痛几乎没有。否则,请测试str2tbl。它应该在Oracle 8及更高版本中工作。
PS :仅出于完整性考虑,我遇到了一篇有关绑定var的不错的文章,该文章涵盖了一些简单的问题,例如虽然没有对IN子句使用变量。