Как я могу создать «динамическое» предложение WHERE? - PullRequest
5 голосов
/ 25 марта 2010

Первое: спасибо!

Я закончил другой проект и большой сюрприз: теперь все работает как надо :-) Спасибо некоторым полезным мыслителям SO!

Итак, я иду со следующим проектом.

Я бы хотел получить что-то вроде этого:

SELECT * FROM tablename WHERE field1=content AND field2=content2 ...

Как вы заметили, это может быть очень длинным предложением where. tablename - это статическое свойство, которое не изменяется. field1, field2, ... (!) И содержимое может измениться.

Так что мне нужна опция для создания оператора SQL в PL / SQL внутри рекурсивной функции. Я действительно не знаю, что искать, поэтому я прошу здесь ссылки или даже слово для поиска ..

Пожалуйста, не начинайте спорить о том, действительно ли рекурсивная функция необходима или в чем ее недостатки - это , а не , о которых идет речь; -)

Если бы вы могли помочь мне создать что-то вроде строки SQL, которая позже сможет успешно выполнить SELECT, это было бы очень здорово!

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

О, еще одна вещь: Я получаю поля и содержимое с помощью xmlType (xmldom.domdocument и т. Д.). Я могу получить поле и содержимое, например, в сгустке из xmltype

Ответы [ 5 ]

4 голосов
/ 25 марта 2010

Цель состоит в том, чтобы динамически собрать оператор из переменного числа фильтров в предложении WHERE. Я не уверен, где рекурсия вписывается во все это, поэтому я просто буду использовать массив для обработки параметров:

SQL> create type qry_param as object
  2      (col_name varchar2(30)
  3      , col_value varchar(20))
  4  /

Type created.

SQL> create type qry_params as table of qry_param
  2  /

Type created.

SQL> 

Эта таблица передается функции, которая зацикливается вокруг массива. Для каждой записи в массиве он добавляет строку к предложению WHERE в формате = ''. Возможно, вам потребуется более сложная фильтрация - различные операторы, явное преобразование типов данных, связывание переменных - но это общая идея.

SQL> create or replace function get_emps
  2      (p_args in qry_params )
  3      return sys_refcursor
  4  as
  5      stmt varchar2(32767);
  6      rc sys_refcursor;
  7  begin
  8      stmt := ' select * from emp';
  9      for i in p_args.first()..p_args.last()
 10      loop
 11          if i = 1 then
 12              stmt := stmt || ' where ';
 13          else
 14              stmt := stmt || ' and ';
 15          end if;
 16          stmt := stmt || p_args(i).col_name
 17                       ||' = '''||p_args(i).col_value||'''';
 18      end loop;
 19      open rc for stmt;
 20      return rc;
 21  end get_emps;
 22  /

Function created.

SQL> 

Наконец, для выполнения этого запроса нам нужно заполнить локальную переменную типа массива и вернуть результат в курсор ref.

SQL> var l_rc refcursor
SQL> declare
  2      l_args qry_params := qry_params
  3                             (qry_param('DEPTNO', '50')
  4                                     , qry_param('HIREDATE', '23-MAR-2010'));
  5  begin
  6      :l_rc := get_emps(l_args);
  7  end;
  8  /

PL/SQL procedure successfully completed.


SQL> print l_rc

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
      8041 FEUERSTEIN PLUMBER         7839 23-MAR-10       4250                    50
      8040 VERREYNNE  PLUMBER         7839 23-MAR-10       4500                    50

SQL>    

редактировать

В последнем абзаце своего вопроса ОП говорит, что они используют XML для соответствия критериям. Это требование не меняет кардинально форму моей первоначальной реализации. Цикл просто должен прогнать запрос XPath вместо массива:

SQL> create or replace function get_emps
  2      (p_args in xmltype )
  3      return sys_refcursor
  4  as
  5      stmt varchar2(32767);
  6      rc sys_refcursor;
  7  begin
  8      stmt := ' select * from emp';
  9      for i in (select * from xmltable (
 10                       '/params/param'
 11                       passing p_args
 12                       columns
 13                           position for ordinality
 14                           , col_name varchar2(30) path '/param/col_name'
 15                           , col_value varchar2(30) path '/param/col_value'
 16                       )
 17               )
 18      loop
 19          if i.position = 1 then
 20            stmt := stmt || ' where ';
 21          else
 22            stmt := stmt || ' and ';
 23          end if;
 24          stmt := stmt || i.col_name
 25                     ||' = '''||i.col_value||'''';
 26      end loop;
 27      open rc for stmt;
 28      return rc;
 29  end get_emps;
 30  /

Function created.

SQL>

Как видно, эта версия возвращает те же результаты, что и раньше ...

SQL> var l_rc refcursor
SQL> declare
  2      l_args xmltype := xmltype
  3                              ('<params>
  4                                  <param>
  5                                      <col_name>DEPTNO</col_name>
  6                                      <col_value>50</col_value>
  7                                  </param>
  8                                  <param>
  9                                      <col_name>HIREDATE</col_name>
 10                                      <col_value>23-MAR-2010</col_value>
 11                                  </param>
 12                              </params>');
 13  begin
 14    :l_rc := get_emps(l_args);
 15  end;
 16  /

PL/SQL procedure successfully completed.

SQL> print l_rc

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
      8041 FEUERSTEIN PLUMBER         7839 23-MAR-10       4250                    50
      8040 VERREYNNE  PLUMBER         7839 23-MAR-10       4500                    50

SQL>
3 голосов
/ 25 марта 2010

Полезный способ использовать динамический SQL, как показано в других ответах и ​​все еще использовать переменные связывания (что является хорошей практикой), - это использовать предложение WITH для связывания переменных. Это служит двум целям: во-первых, оно позволяет вам связывать все свои переменные каждый раз, используете ли вы их или нет; во-вторых, он позволяет вам ссылаться на ваши привязки по имени, поэтому, если вам нужно ссылаться на одну привязку более одного раза, вам все равно придется связывать ее только один раз.

Пример:

create or replace sample_function (
   v_field1 tablename.field1%type default 1,
   v_field2 tablename.field2%type default null,
   v_field3 tablename.field3%type default 'some value') is
   v_base_query varchar2(2000) := 
      'with binds as (
          select :bind1 as field1,
                 :bind2 as field2,
                 :bind3 as field3
            from dual)
       select t.field4, b.field3 from tablename t, binds b
       where 1=1 ';
   v_where varchar2(2000);
   cur_tablename sys_refcursor;
begin
   if v_field1 is not null then
      v_where := v_where || ' and t.field1 = b.field1';
   end if;
   if v_field2 is not null then
      v_where := v_where || ' and t.field2 = b.field2';
   end if;
   if v_field3 is not null then
      v_where := v_where || ' and t.field3 <= b.field3';
   end if;
   open cur_tablename for v_base_query || v_where
      using v_field1, v_field2, v_field3;
   return cur_tablename;
end sample_function;
2 голосов
/ 16 апреля 2014

SELECT * FROM emp

ГДЕ (1 = 1 ИЛИ задание = 'Продавец')

AND (1 = 1 OR TO_CHAR(hiredate,'YYYYMMDD') = '19810220')
AND (1 = 0 OR TO_CHAR(hiredate,'YYYYMMDD') > '19820101')
AND (1 = 1 OR sal = 1600); 

http://www.akadia.com/services/dyn_modify_where_clause.html
проверить эту замечательную статью.

2 голосов
/ 25 марта 2010

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

mycur is ref cursor
open mycur for 'select ... from ... where '||dynamic_string
fetch mycur ...

или

вы можете использовать

execute immediate 'select id from ... where '||dynamic_string bulk collect into mylist
where mytype is for example>
Type Mytype is table of number
mylist Mytype;
0 голосов
/ 25 марта 2010

В PLSQL вы можете сделать что-то вроде этого:

declare
  l_statement varchar2(32767);
begin
  l_statement := 'SELECT * FROM tablename WHERE field1=:a AND field2=:b';

  -- you now have you query. Put in the values that you like.
  execute immediate l_statement
  using 'value1','value2';
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...