在 Oracle 11g 中转换动态查询以使用绑定变量

Posted

技术标签:

【中文标题】在 Oracle 11g 中转换动态查询以使用绑定变量【英文标题】:Converting a dynamic query to use bind variables in Oracle 11g 【发布时间】:2015-04-23 13:04:12 【问题描述】:

我有一个动态搜索查询,我想将其转换为使用绑定变量。查询的动态部分在where 子句中,并使用一系列if 语句来构建连接到查询字符串的其余部分的字符串。然后在openstatement 的for 子句中使用该查询,结果集是返回参数。我不太确定如何做到这一点。

这是存储过程:

PROCEDURE run_search(i_unit_id           IN lu_unit.fsu_id%TYPE,
                     i_equipment         IN tbl_component.component%TYPE,
                     i_equipment_status  IN tbl_component.equipment_status%TYPE,
                     i_equipment_type    IN tbl_component.equipment_type%TYPE,
                     i_equipment_subtype IN tbl_component.equipment_sub_type%TYPE,
                     i_system_id         IN tbl_component.system_id%TYPE,
                     i_association_code  IN tbl_component_assc_code.assc_code%TYPE,
                     i_manufacturer      IN lu_component_manu_model.equipment_manufacturer%TYPE,
                     i_manumodel         IN lu_component_manu_model.equipment_model%TYPE,
                     o_results           OUT sys_refcursor)  AS

  v_query VARCHAR2(32767) := '';
  v_where VARCHAR2(32767) := ' 1= 1';

BEGIN

  IF i_unit_id IS NOT NULL THEN 
    v_where := v_where || ' AND unit_id=''' || i_unit_id ||''' ';
  END IF;
  IF i_equipment IS NOT NULL THEN 
    v_where := v_where || ' AND lower(component) LIKE ''%' || lower(i_equipment) ||'%'' ';
  END IF;
  IF i_equipment_status IS NOT NULL THEN 
    v_where := v_where || ' AND equipment_status=''' || i_equipment_status ||''' ';
  END IF;
  IF i_equipment_type IS NOT NULL THEN 
    v_where := v_where || ' AND equipment_type=''' || i_equipment_type ||''' ';
  END IF;
  IF i_equipment_subtype IS NOT NULL THEN 
    v_where := v_where || ' AND equipment_sub_type=''' || i_equipment_subtype ||''' ';
  END IF;
  IF i_system_id IS NOT NULL THEN
    v_where := v_where || ' AND system_id=''' || i_system_id || ''' ';
  END IF;
  IF i_association_code IS NOT NULL THEN
    v_where := v_where || ' AND EXISTS ( select null from tbl_component_assc_code where assc_code = ''' || i_association_code || ''' and component_id = vcs.component_id )';
  END IF;
  IF i_manufacturer IS NOT NULL THEN
    v_where := v_where || ' AND equipment_manufacturer=''' || i_manufacturer || ''' ';
  END IF;
  IF i_manuModel IS NOT NULL THEN
    v_where := v_where || ' AND equipment_model=''' || i_manuModel || ''' ';
  END IF;

  v_query := 
    '     SELECT rownum, results.* '
  ||'     FROM '
  ||'       ( SELECT '
  ||'           count(*) OVER () ' || ' as total_results, '
  ||''
  ||'           site_id, site_display_name, '
  ||'           unit_id, unit_display_name, '
  ||'           system_id, system_display_name, '
  ||'           component_id, component, component_description, equipment_description, '
  ||'           equipment_status, equipment_model, equipment_serial_number, equipment_type, equipment_sub_type, '
  ||'           template_ids '
  ||''
  ||'         FROM vw_component_search '
  ||'         WHERE ' || v_where 
  ||'         ORDER BY unit_display_name, component '
  ||'       ) results '
  ;
  OPEN o_results FOR v_query;
END run_search;

【问题讨论】:

看到这个***.com/questions/29774122/… 【参考方案1】:

您可以在不动态创建查询的情况下编写查询,因此您可以包含所有参数并忽略 NULL 的那些参数(请对其进行分析以测试与动态查询相比是否存在任何性能问题):

PROCEDURE run_search(i_unit_id           IN lu_unit.fsu_id%TYPE,
                     i_equipment         IN tbl_component.component%TYPE,
                     i_equipment_status  IN tbl_component.equipment_status%TYPE,
                     i_equipment_type    IN tbl_component.equipment_type%TYPE,
                     i_equipment_subtype IN tbl_component.equipment_sub_type%TYPE,
                     i_system_id         IN tbl_component.system_id%TYPE,
                     i_association_code  IN tbl_component_assc_code.assc_code%TYPE,
                     i_manufacturer      IN lu_component_manu_model.equipment_manufacturer%TYPE,
                     i_manumodel         IN lu_component_manu_model.equipment_model%TYPE,
                     o_results           OUT sys_refcursor) 
AS
BEGIN
  OPEN o_results FOR
  SELECT rownum,
         results.*
  FROM   ( SELECT count(*) OVER () as total_results,
                  site_id,
                  site_display_name,
                  unit_id,
                  unit_display_name,
                  system_id,
                  system_display_name,
                  component_id,
                  component,
                  component_description,
                  equipment_description,
                  equipment_status,
                  equipment_model,
                  equipment_serial_number,
                  equipment_type,
                  equipment_sub_type,
                  template_ids
           FROM   vw_component_search
           WHERE (   i_unit_id IS NULL
                 OR  unit_id= i_unit_id )
           AND   (   i_equipment IS NULL
                 OR  lower(component) LIKE '%' || lower(i_equipment) || '%' )
           AND   (   i_equipment_status IS NULL
                 OR  equipment_status= i_equipment_status )
           AND   (   i_equipment_type IS NULL
                 OR  equipment_type= i_equipment_type )
           AND   (   i_equipment_subtype IS NULL
                 OR  equipment_sub_type= i_equipment_subtype )
           AND   (   i_system_id IS NULL
                 OR  system_id= i_system_id )
           AND   (   i_association_code IS NULL
                 OR  EXISTS ( select null
                              from   tbl_component_assc_code
                              where  assc_code = i_association_code
                              and component_id = vcs.component_id ) )
           AND   (   i_manufacturer IS NULL
                 OR  equipment_manufacturer= i_manufacturer )
           AND   (   i_manuModel IS NULL
                 OR  equipment_model= i_manuModel )
           ORDER BY unit_display_name, component
         ) results;
END run_search;

(我没有编译上面的代码 - 所以可能会有一些错误)。

【讨论】:

【参考方案2】:

最好的办法是完全避免这种头痛。 'SP 返回结果集' 在 MSSQL2000 等不良遗留事物中是一种常见的做法,但在 Oracle 中是不必要且值得怀疑的。

如果你想这样做,我建议你这样做:

procedure MakeGarbage(value_mask varchar2) return sys_refcursor is
  cur integer;
  stmt varchar2(32000 byte);
  type TParamTable is table of varchar2(1000) index by varchar2(20);
  params TParamTable;
  i varchar2(20);
begin
  stmt := 'select * from table where 1 = 1 ';
  if value_mask is not null then
    stmt := stmt || ' and value like :value_mask ';
    params('value_mask') := value_mask;
  end if;
  ...
  cur := dbms_sql.create_cursor;
  dbms_sql.open_cursor(cur, stmt, dbms_sql.native);
  i := params.first;
  while i is not null loop
    dbms_sql.bind_variable(i, params(i));
    i := params.next(i);
  end loop;
  return dbms_sql.to_ref_cursor(cur);
end;

【讨论】:

终于找到了一个正确理解如何在 Oracle 中创建动态语句的人。你在这里找不到这么多。 你能否更好地解释这个答案,因为我很难理解。 似乎是一个绝妙的答案。我希望这个答案的作者可以多解释一点,以便我(作为初学者)可以更好地理解。【参考方案3】:

对 PL/SQL 变量的每个引用实际上都是一个绑定变量。

你可以查看这个 asktom 链接 https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:2320123769177

并从此链接http://www.akadia.com/services/ora_bind_variables.html检查“动态SQL”

【讨论】:

以上是关于在 Oracle 11g 中转换动态查询以使用绑定变量的主要内容,如果未能解决你的问题,请参考以下文章

『ORACLE』 PLSQL动态游标的使用(11g)

在 SQL 语句中外连接绑定变量 (Oracle 11g)

Oracle - 使用动态查询插入表

可以将绑定变量连接到动态 SQL WHERE 子句以添加 AND 语句吗?

Oracle 11g 中的限制子句

执行 Oracle Ad Hoc 查询时绑定参数出错; ORA-00907: 缺少右括号