Oracle EXECUTE IMMEDIATE с переменным числом возможных связей? - PullRequest
10 голосов
/ 17 июня 2009

Мне нужно использовать динамическое выполнение SQL в Oracle, где я не знаю точного числа переменных связывания, используемых в SQL до выполнения.

Есть ли способ использовать переменное число переменных связывания при вызове EXECUTE IMMEDIATE каким-либо образом?

Точнее, мне нужно передать один параметр в неизвестный SQL, но я не знаю, как часто он будет там использоваться.

Я пробовал что-то вроде

EXECUTE IMMEDIATE 'SELECT SYSDATE FROM DUAL WHERE :var = :var' USING 1;

Но он отбросил назад с ORA-01008: not all variables bound.

Ответы [ 5 ]

9 голосов
/ 17 июня 2009

Вы не можете сделать это с EXECUTE IMMEDIATE. Однако вы можете сделать это, используя пакет Oracle DBMS_SQL. Руководство разработчика приложений базы данных содержит сравнение между EXECUTE IMMEDIATE, с которым вы знакомы, и dbms_sql методами. Эта страница документирует DBMS_SQL, но содержит несколько примеров (ссылки выше), которые должны помочь вам начать (пример 1 - это простой случай выполнения оператора, который может иметь произвольное количество переменных связывания). DBMS_SQL намного сложнее с точки зрения кодирования, но это позволит вам делать практически все, что вы можете придумать.

Допускается наличие нескольких экземпляров переменной связывания в SQL. Однако вам нужно знать имя, используемое в качестве переменной связывания (например,: var в вашем случае), чтобы передать его в DBMS_SQL.BIND_VARIABLE.

7 голосов
/ 07 августа 2009

Вы также можете обойти эту проблему, используя оператор WITH. Обычно лучше использовать DBMS_SQL, но иногда это проще:

BEGIN
    EXECUTE IMMEDIATE 'WITH var AS (SELECT :var FROM dual) SELECT SYSDATE FROM DUAL WHERE (SELECT * FROM var) = (SELECT * FROM var)' USING 1;
END;
2 голосов
/ 17 июня 2009

Эта тема на AskTom охватывает предмет в деталях.

В вашем случае, если вы хотите передать один параметр или ни одного, вы можете создать два запроса, которые имеют один параметр, и в одном из них он не используется (т. Е. Предикат всегда истинен), например:

-- query1
SELECT * FROM DUAL WHERE dummy = :x;

-- query2
SELECT * FROM DUAL WHERE nvl(:x, 1) IS NOT NULL;

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

1 голос
/ 19 мая 2011

Точнее, мне нужно передать один параметр в неизвестный SQL, но я не знаю, как часто он будет там использоваться.

Я на самом деле столкнулся с той же самой проблемой пару дней назад, и мой друг поделился со мной способом сделать именно это с EXECUTE IMMEDIATE.

Он включает в себя создание блока PLSQL, а не самого блока SQL. При использовании EXECUTE IMMEDIATE с блоком кода PLSQL вы можете связывать переменные по имени, а не по положению.

Посмотрите мой пример / код и мою аналогичную ветку вопросов / ответов:

1 голос
/ 18 июня 2009

Можно использовать dbms_sql, как объяснил Стив Броберг, но результирующий курсор нельзя использовать (читать) у многих клиентов. В Oracle 11 добавлена ​​функция преобразования (dbms_sql.to_refcursor), которая позволяет преобразовать курсор dbms_sql в курсор ref, но по какой-то причине этот преобразованный курсор ref нельзя использовать в .Net приложение. Можно использовать обычный ref-курсор в .net, но не ref-курсор, который раньше был dbms_sql -курсором.

Так, какой клиент будет использовать этот курсор?

...