2012-01-23 1 views
3

В PL/SQL, можно указать значения для оператора IN с помощью конкатенации:Как вы определяете предложение IN в динамическом запросе с использованием переменной?

v_sql := 'select field1 
from table1 
where field2 in (' || v_list || ')'; 

Можно ли сделать то же самое с помощью переменной?

v_sql := 'select field1 
from table1 
where field2 in (:v_list)'; 

Если да, то как?

EDIT: Что касается ответа Марцина, как выбрать из результирующей таблицы?

declare 

cursor c_get_csv_as_tables is 
select in_list(food_list) food_list 
from emp_food 
where emp_type = 'PERM'; 

cursor c_get_food_list (v_food_table varchar2Table)is 
select * 
from v_food_table; 

begin 
    for i in c_get_csv_as_tables loop 
     for j in c_get_food_list(i.food_list) loop 
      dbms_output.put_line(j.element); 
     end loop; 
    end loop; 
end; 

Я получаю следующее сообщение об ошибке:

ORA-06550: line 10, column 6: 
PL/SQL: ORA-00942: table or view does not exist 
ORA-06550: line 9, column 1: 
PL/SQL: SQL Statement ignored 
ORA-06550: line 15, column 34: 
PLS-00364: loop index variable 'J' use is invalid 
ORA-06550: line 15, column 13: 
PL/SQL: Statement ignored 
+0

возможно дубликат [PL/SQL использование VARRAY в в Пункте] (http://stackoverflow.com/questions/8476610/pl- sql-use-varray-in-in-clause) – Sathya

+0

@ Сатья, я не понимаю ... как это так? Мне нужно передать переменную связывания. Есть ли аналогичный способ сделать это? – Zesty

+0

Я не понимал, что вы хотите связать. сказав, что, afaik, вы не можете использовать такую ​​переменную связывания. – Sathya

ответ

7

Как и в @Sathya ссылке, вы можете связать VARRAY (я взял пример @Codo):

CREATE OR REPLACE TYPE str_tab_type IS VARRAY(10) OF VARCHAR2(200); 
/
DECLARE 
    l_str_tab str_tab_type; 
    l_count NUMBER; 
    v_sql varchar2(3000); 
BEGIN 
    l_str_tab := str_tab_type(); 
    l_str_tab.extend(2); 
    l_str_tab(1) := 'TABLE'; 
    l_str_tab(2) := 'INDEX'; 

    v_sql := 'SELECT COUNT(*) FROM all_objects WHERE object_type IN (SELECT COLUMN_VALUE FROM TABLE(:v_list))'; 

    execute immediate v_sql into l_count using l_str_tab; 

    dbms_output.put_line(l_count); 
END; 
/

UPDATE: первая команда может быть заменена:

CREATE OR REPLACE TYPE str_tab_type IS TABLE OF VARCHAR2(200); 
    /

затем звонить:

l_str_tab.extend(1); 

, когда вы добавляете значение

+0

А теперь я понимаю. Но это решение, похоже, требует от нас знать максимальное количество значений для установки длины массива. Он также требует преобразования строки CSV в массив. Второе может быть сделано, но первое должно быть сложно. – Zesty

+1

Я использовал VARRAY только потому, что он был в оригинальном примере. Вы можете использовать другую коллекцию, такую ​​как ТАБЛИЦА VARCHAR2 (200). Метод расширения можно вызывать для каждого добавленного значения (при преобразовании CSV) –

2

К сожалению, вы не можете связать список, как это, однако вы можете использовать табличную функцию. Читать this

Вот пример использования на основе кода:

declare 

cursor c_get_csv_as_tables is 
select in_list(food_list) food_list 
from emp_food 
where emp_type = 'PERM'; 

cursor c_get_food_list (v_food_table varchar2Table)is 
select column_value food 
from TABLE(v_food_table); 

begin 
    for i in c_get_csv_as_tables loop 
     for j in c_get_food_list(i.food_list) loop 
      dbms_output.put_line(j.food); 
     end loop; 
    end loop; 
end; 

я использовал здесь column_value псевдостолбец

+0

Спасибо! Я подожду в течение дня, чтобы узнать, есть ли какие-либо другие решения, и затем отметьте это как решение. – Zesty

+0

У меня возникла проблема с этим подходом. Как выбрать из результирующей таблицы, так как у меня нет имени столбца? – Zesty

1

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

Проще говоря, вы не можете использовать переменную привязки для таблицы или столбца. Мало того, что связывать переменные, они считаются символом, поэтому, если вы хотите использовать номер, вы должны использовать to_number(:b1) и т. Д.

Здесь ваш запрос падает. Когда вы передаете строку, Oracle предполагает, что весь ваш список является одной строкой. Таким образом, вы эффективно работаете:

select field1 
    from table1 
where field2 = v_list 

Нет причин, по которым вы не можете сделать это иначе. Я предполагаю, что вы динамически создаете v_list, что означает, что все, что вам нужно сделать, это создать этот список по-разному. Серия or условий, предположительно :-), не отличается от использования in.

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

SQL> set serveroutput on 
SQL> 
SQL> declare 
    2 
    3 l_string varchar2(32767); 
    4 l_count number; 
    5 
    6 begin 
    7 
    8  for xx in (select rownum as rnum, a.* 
    9     from user_tables a 
10     where rownum < 20) loop 
11 
12  if xx.rnum = 1 then 
13   l_string := 'table_name = ''' || xx.table_name || ''''; 
14  else 
15   l_string := l_string || ' or table_name = ''' || xx.table_name || ' 
'''; 
16  end if; 
17 
18  end loop; 
19 
20  execute immediate 'select count(*) 
21       from user_tables 
22       where ' || l_string 
23       into l_count 
24        ; 
25 
26  dbms_output.put_line('count is ' || l_count); 
27 
28 end; 
29/
count is 19 

PL/SQL procedure successfully completed. 
+0

Отличное альтернативное решение! – Zesty

2

Переменная Bind может использоваться в Oracle SQL-запросе с предложением «in».

Работы в 10г; Я не знаю о других версиях.

Переменная привязки - varchar длиной до 4000 символов.

Пример: Связать переменную, содержащую список значений, разделенных запятыми, например.

: bindvar = 1,2,3,4,5

select * from mytable 
    where myfield in 
    (
     SELECT regexp_substr(:bindvar,'[^,]+', 1, level) items 
     FROM dual 
     CONNECT BY regexp_substr(:bindvar, '[^,]+', 1, level) is not null 
    );