我怎样才能摆脱动态SQL

时间:2009-03-22 12:26:55

标签: sql oracle plsql

我的一个包体

中有以下动态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语句。有没有聪明的方法来实现这个目标?

5 个答案:

答案 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)

您可以通过将值批量复制到临时表中并加入其中来解决此问题。一开始可能听起来有点矫枉过正,但由于这是一种非常常见的模式,你最终会经常重复使用该代码。