我有一个动态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()
答案 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_name
和last_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 > 0
与DUAL
一起使用对性能非常重要,因为它会强制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;