动态PL / SQL中的绑定变量

时间:2010-08-31 05:52:49

标签: oracle plsql dynamic-sql

我有一个动态PL / SQL,它将根据用户输入的搜索条件构建SELECT语句,如下:

     l_sql := 'SELECT *  INTO  FROM TABLEA WHERE 1=1 ';

      IF in_param1 IS NOT NULL THEN
        l_sql := l_sql || 'AND column1 = in_param1 ';
      END IF;

      IF in_param2 IS NOT NULL THEN
        l_sql := l_sql || 'AND column2 = in_param2 ';
      END IF;
      ...................................

     IF in_paramXX IS NOT NULL THEN
        l_sql := l_sql || 'AND columnXX = in_paramXX ';
      END IF;

为了减少硬分析开销,我考虑使用绑定变量。但是,在将实际值提供给绑定变量时很难管理,因为有太多绑定变量和生成的SELECT语句的组合。我无法使用http://www.dba-oracle.com/plsql/t_plsql_dynamic_binds.htm中引入的DBMS_SESSION.set_context()方法,因为我的帐户无权使用此包。此外,我希望生成的SQL仅包含用户未留空的字段的条件。所以我无法将动态SQL更改为类似

SELECT *  INTO  FROM TABLEA WHERE 1=1
and ( in_param1 is NULL or  column1 = in_param1)
and ( in_param2 is NULL or  column2 = in_param2)
...............................................
and ( in_paramXX is NULL or  columnXX = in_paramXX)

所以,我想尝试使用DBMS_SQL方法。任何人都可以举例说明如何使用DBMS_SQL用绑定变量调用动态SQL?特别是,如何从DBMS_SQL.execute()执行结果到SYS_REFCURSOR,如:

open refcursor for select .... from

我使用的oracle版本是10g,似乎oracle 10g没有DBMS_Sql.To_Refcursor()

2 个答案:

答案 0 :(得分:3)

在Oracle版本中,您可以对查询应用一些技巧来执行此操作。我们的想法是使用以下形式的查询:

select *
from
(select
 :possibleParam1 as param1
 -- do the same for every possible param in your query
 :possibleParamN as paramN
 from dual
 where rownum > 0) params
 inner join 
-- join your tables here
on 
-- concatenate your filters here
where
-- fixed conditions

然后执行:

open c for query using param1, ..., paramN;

它的工作原理是使用DUAL为每个param生成一个假行,然后使用您想要应用的过滤器将这个假行连接到您的真实查询(没有任何过滤器)。这样,您在SELECT子查询的params列表中有一个固定的绑定变量列表,但可以通过修改params与您的实际查询之间的连接条件来控制应用哪些过滤器

所以,如果你有类似的话,请说:

create table people (
    first_name varchar2(20)
    last_name varchar2(20)
);

如果您只想对first name

进行过滤,则可以构建以下查询
select *
from
(select
 :first_name as first_name,
 :last_name as last_name
 from dual
 where rownum > 0) params
 inner join 
people
on 
people.first_name = params.first_name;

如果您要同时对first_namelast_name

进行过滤,请执行此操作
select *
from
(select
 :first_name as first_name,
 :last_name as last_name
 from dual
 where rownum > 0) params
 inner join 
people
on 
people.first_name = params.first_name and
people.last_name = params.last_name;

并且在每种情况下都将使用

执行
open c for query using filterFirstName, filterLastName;

where rownum > 0DUAL一起使用对性能非常重要,因为它会强制Oracle“实现”子查询。这通常会使DUAL停止干扰查询的其余部分。无论如何,你应该检查执行计划,以确保Oracle没有做错任何事。

答案 1 :(得分:0)

在10g中,DBMS_SQL游标不能更改为Ref Cursor。通过DBMS_SQL遍历结果集是曲折的,因为除了循环遍历行之外,还必须遍历行中的列。

  

我希望生成的SQL只包含   关于这些领域的条件   用户没有留空

纯粹是出于性能原因吗?如果是这样,我建议你弄清楚实际的执行计划是什么,并为它们使用单​​独的查询。

例如,假设我正在搜索人员,参数是first_name,last_name。性别,date_of_birth。该表具有索引(last_name,first_name)和(date_of_birth),因此我只想允许查询,如果它指定last_name或date_of_birth。

IF :p_firstname IS NOT NULL and :p_lastname IS NOT NULL THEN
  OPEN cur FOR 
    'SELECT * FROM PEOPLE WHERE last_name=:a AND first_name=:b AND
     (date_of_birth = :c or :c is NULL) AND (gender = :d or :d IS NULL)' USING ....
ELSIF :p_lastname IS NOT NULL THEN
  OPEN cur FOR 
    'SELECT * FROM PEOPLE WHERE last_name=:a AND 
     (date_of_birth = :c or :c is NULL) AND (gender = :d or :d IS NULL)' USING ....
ELSIF :p_dateofbirth IS NOT NULL THEN
  OPEN cur FOR 
    'SELECT * FROM PEOPLE WHERE date_of_birth=:a AND 
     (first_name=:b OR :b IS NULL) AND (gender = :d or :d IS NULL)' USING ....
ELSE
  RAISE_APPLICATION_ERROR(-20001,'Last Name or Date of Birth MUST be supplied);
END IF;