我的一个包体
中有以下动态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语句。有没有聪明的方法来实现这个目标?
答案 0 :(得分:6)
我猜你知道你可以将变量绑定到PI_N_Dept
以删除那条动态sql。不幸的是,您的IN
子句和sExcludeCategories
不能为Oracle中的列表绑定变量(据我所知至少高达9.2)
你有几个选择。您当前的解决方案是最简单的。另一种解决方案是更改过程以接受多个变量并创建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中的某个值(注意:小心并在此处测试空值)
Ask Tom中提供了另一种解决方案。这看起来很简单,虽然我还没有测试过。它的工作原理是创建一个函数str2tbl(),它允许你传递一系列用逗号分隔的数字,并通过dual创建一个'table'来做一个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以处理引号(如果它们包含在变量中(并且您无法更改)并将myTableType
的类型更改为varchar2(10)
或更合适的内容。
总的来说,如果原始的sql不影响性能,那么为了简单起见,我将它留作动态SQL。维持头脑的痛苦要小得多。否则测试出str2tbl。它应该在Oracle 8及更高版本中有效。
PS :为了完整起见,我遇到this nice article on binding vars,它涵盖了简单的问题,比如不使用IN子句的变量。
答案 1 :(得分:0)
我不知道oracle,但在SQL Server中,通常会获得一个“拆分”udf(like so - 只是许多可用版本中的一个),它将CSV转换为一列值,并加入(等)到它。 pl-sql有类似的东西吗?
答案 2 :(得分:0)
create or replace type numbertype
as object
(nr number(20,10) )
/
create or replace type number_table
as table of numbertype
/
create or replace procedure tableselect
( p_numbers in number_table
, p_ref_result out sys_refcursor)
is
begin
open p_ref_result for
select *
from employees , (select /*+ cardinality(tab 10) */ tab.nr from table(p_numbers) tab) tbnrs
where id = tbnrs.nr;
end;
/
答案 3 :(得分:0)
你可以这样写:
OPEN ccur for
select c.category
from test_category c
where
c.deptid= PI_N_Dept
and c.category not in
(select category_id from categories where <some-condition-that-finds-the-categories-that-should-be-excluded>);
答案 4 :(得分:0)
您可以通过将值批量复制到临时表中并加入其中来解决此问题。一开始可能听起来有点矫枉过正,但由于这是一种非常常见的模式,你最终会经常重复使用该代码。