2010-08-31 113 views
1

我有一個動態的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

在10g中,DBMS_SQL遊標不能更改爲Ref遊標。通過DBMS_SQL瀏覽結果集是曲折的,因爲除了遍歷行之外,還必須遍歷行中的列。

我想生成的SQL只包含 上的字段的 用戶沒有留下空

的條件是純粹出於性能的考慮?如果是這樣,我建議你找出實際的執行計劃是什麼,併爲他們使用單獨的查詢。

例如,假設我正在搜索人,並且參數是first_name,last_name。性別,日期。該表的索引在(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; 
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,然後內側連接這假的行到你真正的查詢(沒有任何過濾器)只使用你想應用的過濾器。這樣,params子查詢的SELECT列表中有一個固定的綁定變量列表,但可以通過修改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因爲它迫使甲骨文重要的是執行以「兌現」的子查詢。這通常會使DUAL停止干擾查詢的其餘部分。無論如何,你應該檢查執行計劃以確保Oracle沒有做錯任何事情。