Связывание переменных в динамическом PL / SQL - PullRequest
1 голос
/ 31 августа 2010

У меня есть динамический PL / SQL, который будет строить инструкцию SELECT на основе того, что критерии поиска, введенные пользователями, нравится:

     l_sql := 'SELECT *  INTO  FROM TABLEA WHERE 1=1 ';

      IF in_param1 IS NOT NULL THEN
        l_sql := l_sql || 'AND column1 = in_param1 ';
      END IF;

      IF in_param2 IS NOT NULL THEN
        l_sql := l_sql || 'AND column2 = in_param2 ';
      END IF;
      ...................................

     IF in_paramXX IS NOT NULL THEN
        l_sql := l_sql || 'AND columnXX = in_paramXX ';
      END IF;

Чтобы уменьшить затраты на жесткий анализ, я рекомендую использовать переменные привязки. Однако при передаче фактических значений переменным привязки сложно управлять, поскольку существует так много переменных привязки и комбинации сгенерированного оператора SELECT. Я не могу использовать метод DBMS_SESSION.set_context (), представленный в http://www.dba -oracle.com / plsql / t_plsql_dynamic_binds.htm , поскольку моя учетная запись не имеет права использовать этот пакет. Кроме того, я хочу, чтобы сгенерированный SQL содержал только те условия на полях, которые пользователь не оставил пустыми. Поэтому я не могу изменить динамический SQL на что-то вроде

SELECT *  INTO  FROM TABLEA WHERE 1=1
and ( in_param1 is NULL or  column1 = in_param1)
and ( in_param2 is NULL or  column2 = in_param2)
...............................................
and ( in_paramXX is NULL or  columnXX = in_paramXX)

Итак, я хочу попробовать использовать метод DBMS_SQL. Может ли кто-нибудь привести пример того, как использовать DBMS_SQL для вызова динамического SQL с переменными привязки? В частности, как я могу получить результат, выполненный из DBMS_SQL.execute () в SYS_REFCURSOR, что-то вроде:

open refcursor for select .... from

Используемая мной версия oracle - 10g, и кажется, что в oracle 10g нет DBMS_Sql.To_Refcursor ()

Ответы [ 2 ]

3 голосов
/ 18 января 2011

В вашей версии Oracle вы можете применить некоторые хитрости к вашему запросу, чтобы сделать это.Идея состоит в том, чтобы использовать запрос в следующей форме:

select *
from
(select
 :possibleParam1 as param1
 -- do the same for every possible param in your query
 :possibleParamN as paramN
 from dual
 where rownum > 0) params
 inner join 
-- join your tables here
on 
-- concatenate your filters here
where
-- fixed conditions

, а затем выполнить его с помощью:

open c for query using param1, ..., paramN;

Он работает с использованием DUAL для создания поддельной строки с каждымparam, затем внутреннее соединение этой фиктивной строки с вашим реальным запросом (без каких-либо фильтров), используя только те фильтры, которые вы хотите применить.Таким образом, у вас есть фиксированный список переменных связывания в списке SELECT подзапроса params, но вы можете контролировать, какие фильтры применяются, изменяя условие соединения между params и вашим реальным запросом.

Итак, если у вас есть что-то вроде, скажем:

create table people (
    first_name varchar2(20)
    last_name varchar2(20)
);

, вы можете построить следующий запрос, если вы просто хотите фильтровать по first name

select *
from
(select
 :first_name as first_name,
 :last_name as last_name
 from dual
 where rownum > 0) params
 inner join 
people
on 
people.first_name = params.first_name;

, и это, если вы хотитефильтровать как first_name, так и last_name

select *
from
(select
 :first_name as first_name,
 :last_name as last_name
 from dual
 where rownum > 0) params
 inner join 
people
on 
people.first_name = params.first_name and
people.last_name = params.last_name;

, и в каждом случае вы выполняете с

open c for query using filterFirstName, filterLastName;

Для производительности важно использовать where rownum > 0 с DUAL, поскольку это заставляет Oracle «материализовать» подзапрос.Это обычно заставляет DUAL прекратить вмешиваться в остальную часть запроса.В любом случае, вам следует проверить планы выполнения, чтобы убедиться, что Oracle не делает ничего плохого.

0 голосов
/ 01 сентября 2010

В 10g курсор DBMS_SQL нельзя изменить на Ref Cursor. Просмотр набора результатов через DBMS_SQL является сложным процессом, поскольку, помимо циклического перемещения по строкам, вам также необходимо циклически проходить по столбцам в строке.

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

Это чисто из соображений производительности? Если это так, я предлагаю вам выяснить, каковы практические планы выполнения, и использовать для них отдельные запросы.

Например, скажите, что я ищу людей, и параметры: first_name, last_name. пол, дата рождения Таблица имеет индексы (last_name, first_name) и (date_of_birth), поэтому я хочу разрешить запрос только в том случае, если в нем указано либо last_name, либо date_of_birth.

IF :p_firstname IS NOT NULL and :p_lastname IS NOT NULL THEN
  OPEN cur FOR 
    'SELECT * FROM PEOPLE WHERE last_name=:a AND first_name=:b AND
     (date_of_birth = :c or :c is NULL) AND (gender = :d or :d IS NULL)' USING ....
ELSIF :p_lastname IS NOT NULL THEN
  OPEN cur FOR 
    'SELECT * FROM PEOPLE WHERE last_name=:a AND 
     (date_of_birth = :c or :c is NULL) AND (gender = :d or :d IS NULL)' USING ....
ELSIF :p_dateofbirth IS NOT NULL THEN
  OPEN cur FOR 
    'SELECT * FROM PEOPLE WHERE date_of_birth=:a AND 
     (first_name=:b OR :b IS NULL) AND (gender = :d or :d IS NULL)' USING ....
ELSE
  RAISE_APPLICATION_ERROR(-20001,'Last Name or Date of Birth MUST be supplied);
END IF;
...