Я предполагаю, что вы знаете, вы можете связать переменную PI_N_Dept
, чтобы удалить эту часть динамического SQL. К сожалению, для вашего предложения IN
и sExcludeCategories
вы не можете привязать переменную для списка в Oracle (по крайней мере, до 9,2, насколько я знаю)
У вас есть несколько вариантов. Ваше текущее решение является самым простым. Другим решением является изменение процедуры приема нескольких переменных и создание списка операторов И.
'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
';
или иметь фиксированный список значений
'select c.category
from test_category c
where c.deptid= :PI_N_Dept
and c.category not in (:sExcludeCategory1 , :sExcludeCategory2, :sExcludeCategory3)';
Вы должны быть осторожными в том случае, если вы только хотите 2 категории. Третий должен быть установлен на какое-то значение, не относящееся к c.category (NB: будьте осторожны и проверяйте нулевые значения здесь)
Другое решение представлено в 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: Только для полноты я столкнулся с this nice article on binding vars, который охватывает простые проблемы, например, хотя и не используя переменные для предложений IN.
Большое спасибо за такой отличный ответ. С небольшой настройкой я смогу решить проблему. – Tuxist
В общем, это очень хороший ответ. Однако я категорически не согласен с вашим заявлением о том, чтобы оставить его динамическим SQL. Использование конкатенации строк для создания вашего SQL оставляет вас уязвимыми для атак SQL-инъекций. – chilltemp