【问题标题】:How can I create a "dynamic" WHERE clause?如何创建“动态”WHERE 子句?
【发布时间】:2011-01-31 15:18:23
【问题描述】:

首先:谢谢!

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

所以我开始下一个项目。

我想要这样的东西:

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

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

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

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

如果你能帮助我创建一个类似 SQL-String 的东西,它以后能够成功地执行 SELECT,那就太好了!

我能够通过递归函数每次都生成一个更长的字符串,但我无法从中创建 SQL 语句..

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

【问题讨论】:

    标签: oracle select plsql


    【解决方案1】:

    目标是从 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> 
    

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

    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>
    

    【讨论】:

      【解决方案2】:

      使用其他答案中显示的动态 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;
      

      【讨论】:

      • 这是一个绝妙的答案。我有个问题 。如果这里有连接呢?如果有连接,我们还可以使用“With 子句”吗?例如,如果基本查询是这样的: select a.column_name , b.column name from first_table a inner join second_table b
      • @Pani 是的。 WITH 子句称为公用表表达式,支持所有常规查询语法。
      • 感谢您的回答。我试图在更复杂的查询中使用您的方法,但我无法成功。我在这里问了我的问题,但没有得到实际的答案。我想知道如果你有时间可以看看它们。 dba.stackexchange.com/questions/273069/…,第二个是dba.stackexchange.com/questions/271701/…
      【解决方案3】:

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

      【讨论】:

        【解决方案4】:
        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
        看看这篇精彩的文章。

        【讨论】:

        • 这是一个很好的技巧,但不幸的是,它通常会导致发生全表扫描而不是使用索引,这在许多情况下会导致性能不佳。
        【解决方案5】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-08-28
          • 1970-01-01
          • 2013-05-11
          • 1970-01-01
          • 2013-01-13
          • 1970-01-01
          • 2018-03-08
          • 2019-09-16
          相关资源
          最近更新 更多