如何创建“动态”WHERE子句?

时间:2010-03-25 09:20:43

标签: oracle select plsql

第一:谢谢!

我完成了我的另一个项目并且大惊喜:现在一切正常:-) 感谢一些有用的SO思想家!

所以我在这里继续下一个项目。

我想得到这样的东西:

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

正如您所注意到的,这可能是一个非常长的where子句。 tablename是一个不会改变的静态属性。 field1field2,...(!),内容可以更改。

所以我需要一个选项,在递归函数中在PL / SQL中构建一个SQL语句。 我真的不知道该搜索什么,所以我在这里要求链接甚至一个词来搜索..

请不要开始争论是否真的需要递归函数或它的不足之处 - 这是有问题; - )

如果你能帮助我创建类似SQL-String的东西,以后能够成功完成SELECT,这将是非常好的!

我能够通过递归函数并每次都创建一个更长的字符串,但是我不能从它做一个SQL语句..

哦,还有一件事: 我通过xmlType(xmldom.domdocument等)获取字段和内容我可以从xmltype中获取字段和内容,例如clob

5 个答案:

答案 0 :(得分:4)

该对象是从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> 

此表传递给一个循环数组的函数。对于数组中的每个条目,它以格式< name>格式向WHERE子句附加一行。 ='< value>'。可能你需要更复杂的过滤 - 不同的运算符,显式数据类型转换,绑定变量 - 但这是一般的想法。

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> 

最后要执行此查询,我们需要填充数组类型的局部变量,并将结果返回给引用游标。

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>    

修改

在他们问题的最后一段中,OP表示他们正在使用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>

答案 1 :(得分:3)

使用动态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 :(得分:2)

您可以创建游标,然后动态创建一个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;

答案 3 :(得分:2)

SELECT * FROM emp

WHERE(1 = 1 OR job =&#39; SALESMAN&#39;)

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
看看这篇精彩的文章。

答案 4 :(得分:0)

在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;