Как я могу избавиться от динамического SQL - PullRequest
3 голосов
/ 22 марта 2009

У меня есть следующий динамический 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 ]

6 голосов
/ 22 марта 2009

Полагаю, вы знаете, что вы можете связать переменную с 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 (), которая позволяет передавать ряд чисел, разделенных запятыми, и создавать «таблицу» через дуал для выполнения 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 : Просто для полноты, я наткнулся на эту замечательную статью о связывании переменных , в которой рассматриваются простые проблемы, такие как использование переменных для предложений IN.

0 голосов
/ 22 марта 2009

Вы можете решить эту проблему, скопировав ваши значения во временную таблицу и присоединившись к ней. Поначалу это может показаться немного излишним, но так как это очень распространенный шаблон, вам наверняка придется часто повторять этот код.

0 голосов
/ 22 марта 2009
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; 
/
0 голосов
/ 22 марта 2009

Вы можете написать это так:

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>);
0 голосов
/ 22 марта 2009

Я не знаю оракула, но в SQL Server довольно часто можно получить «разделенный» udf (, например, - только одна из множества доступных версий), который превращает CSV в столбец значений и присоединение (и т. д.) к нему. У pl-sql есть что-нибудь подобное?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...