简体   繁体   English

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

[英]How can I create a “dynamic” WHERE clause?

First: Thanks!第一:谢谢!

I finished my other project and the big surprise: now everything works as it should :-) Thanks to some helpful thinkers of SO!我完成了我的另一个项目和一个大惊喜:现在一切正常:-) 感谢 SO 的一些有用的思想家!

So here I go with the next project.所以我开始下一个项目。

I'd like to get something like this:我想得到这样的东西:

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

As you noticed this can be a very long where-clause.正如您所注意到的,这可能是一个很长的 where 子句。 tablename is a static property which does not change. tablename 是一个静态属性,不会改变。 field1 , field2 , ... (!) and the contents can change. field1 , field2 , ... (!) 并且内容可以改变。

So I need an option to build up a SQL statement in PL/SQL within a recursive function.所以我需要一个选项来在递归函数内在 PL/SQL 中构建 SQL 语句。 I dont really know what to search for, so I ask here for links or even a word to search for..我真的不知道要搜索什么,所以我在这里要求链接甚至一个词来搜索..

Please dont start to argue about wether the recursive function is really needed or what its disadvanteges - this is not in question ;-)请不要开始争论递归函数是否真的需要或它的缺点是什么 - 这不是问题;-)

If you could help me to create something like an SQL-String which will later be able to do a successful SELECT this would be very nice!如果你能帮我创建一个类似 SQL-String 的东西,它以后将能够成功执行 SELECT,这将是非常好的!

Iam able to go through the recursive function and make a longer string each time, but I cannot make an SQL statement from it..我能够通过递归函数并每次生成更长的字符串,但我无法从中生成 SQL 语句。

Oh, one additional thing: I get the fields and contents by a xmlType (xmldom.domdocument etc) I can get the field and the content for example in a clob from the xmltype哦,还有一件事:我通过 xmlType(xmldom.domdocument 等)获取字段和内容我可以从 xmltype 获取字段和内容,例如在 clob 中

The object is to dynamically assemble a statement from a variable number of filters in the WHERE clause.目标是从 WHERE 子句中的可变数量的过滤器中动态地组装一条语句。 I'm not sure where recursion fits into all this, so I will just use an array to handle the parameters:我不确定递归在哪里适合所有这些,所以我将只使用一个数组来处理参数:

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> 

This table is passed to a function, which loops around the array.该表被传递给一个函数,该函数在数组周围循环。 For each entry in the array it appends a line to the WHERE clause in the format <name> = '<value>'.对于数组中的每个条目,它以 <name> = '<value>' 格式将一行附加到 WHERE 子句。 Probably you will require more sophisticated filtering - different operators, explicit data type conversion, bind variables - but this is the general idea.可能您需要更复杂的过滤——不同的运算符、显式数据类型转换、绑定变量——但这是总体思路。

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> 

Finally to execute this query we need to populate a local variable of the array type and return the result to a ref cursor.最后,为了执行这个查询,我们需要填充一个数组类型的局部变量并将结果返回给一个引用游标。

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>    

edit编辑

In the last paragraph of their question the OP says they are using XML to pass the criteria.在他们问题的最后一段中,OP 表示他们正在使用 XML 来通过标准。 This requirement doesn't dramatically change the shape of my original implementation.这个要求不会显着改变我原始实现的形状。 The loop simply needs to drive off an XPath query instead of an array:循环只需要驱动一个 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>

As can be seen, this version returns the same results as before...可以看出,这个版本返回的结果和以前一样......

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>

A useful way to use the dynamic SQL as shown in the other answers and still use bind variables (which is a good practice) is to use a WITH clause to bind the variables.使用其他答案中显示的动态 SQL 并仍然使用绑定变量(这是一个很好的做法)的一种有用方法是使用 WITH 子句来绑定变量。 This serves two purposes: first, it lets you bind all of your variables every time, whether you're using them or not;这有两个目的:首先,它让您每次都绑定所有变量,无论您是否使用它们; second, it allows you to refer to your binds by name, so if you need to reference one more than once, you still only have to bind it once.其次,它允许您按名称引用您的绑定,因此如果您需要多次引用一个,您仍然只需绑定一次。

An example:一个例子:

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;
SELECT * FROM emp
  
WHERE (1 = 1 OR job = 'SALESMAN')

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 http://www.akadia.com/services/dyn_modify_where_clause.html
check out this wonderful article.看看这篇精彩的文章。

You can create a cursor and then create a sql string dynamically and then use您可以创建一个游标,然后动态创建一个 sql 字符串,然后使用

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

or或者

you can use您可以使用

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;

In PLSQL you can do something like this:在 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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM