2010-08-31 7 views
1

J'ai un PL/SQL dynamique qui construira l'instruction SELECT en fonction de ce que l'entrée des critères de recherche des utilisateurs, aime:des variables de liaison dans PL/SQL dynamique

 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; 

Pour réduire les frais généraux d'analyse syntaxique dur, Je considère utiliser les variables de liaison. Cependant, il est difficile à gérer lors de la fourniture des valeurs réelles aux variables de liaison car il existe un grand nombre de variables de liaison et une combinaison de l'instruction SELECT générée. Je ne peux pas utiliser la méthode de DBMS_SESSION.set_context() présentée au http://www.dba-oracle.com/plsql/t_plsql_dynamic_binds.htm car mon compte n'a pas le droit d'utiliser ce package. De plus, je veux que le SQL généré ne contienne que les conditions sur les champs que l'utilisateur n'a pas laissés vides. Je ne peux pas changer le SQL dynamique à quelque chose aime

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) 

Alors, je veux essayer d'utiliser la méthode DBMS_SQL .Peut-on donner un exemple sur la façon d'utiliser DBMS_SQL pour appeler SQL dynamique avec des variables de liaison? Surtout, comment puis-je obtenir le résultat exécuté à partir DBMS_SQL.execute() à la SYS_REFCURSOR, quelque chose comme:

open refcursor for select .... from 

La version oracle que j'utilise est 10g et il semble que l'oracle 10g ne pas DBMS_Sql.To_Refcursor()

Répondre

0

Dans 10g, un curseur DBMS_SQL ne peut pas être modifié en un curseur de référence. Traverser un ensemble de résultats via DBMS_SQL est tortueux car, en plus de parcourir les lignes, vous devez également parcourir les colonnes d'affilée.

Je veux le seul SQL généré contient les conditions sur les champs que l'utilisateur n'a pas laissé vide

Est-ce uniquement pour des raisons de performance? Si c'est le cas, je vous suggère de définir les plans d'exécution pratiques et d'utiliser des requêtes distinctes pour eux. Par exemple, disons que je recherche sur des personnes et que les paramètres sont prénom, nom_famille. sexe, date_de_naissance. La table a des index sur (last_name, first_name) et (date_of_birth), donc je veux seulement autoriser une requête si elle spécifie last_name ou 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

Dans votre version Oracle, vous pouvez appliquer quelques astuces à votre requête pour ce faire. L'idée est d'utiliser une requête sous la forme suivante:

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 

exécuter ensuite avec:

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

Il fonctionne en utilisant DUAL pour générer une ligne de faux avec chaque PARAM et rejoindre intérieur cette fausse ligne à votre requête réelle (sans aucun filtre) en utilisant uniquement les filtres que vous souhaitez appliquer. De cette façon, vous avez une liste fixe de variables de liaison dans la liste SELECT de la sous-requête params, mais pouvez contrôler quels filtres sont appliqués en modifiant la condition de jointure entre params et votre requête réelle.

Donc, si vous avez quelque chose comme, par exemple:

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

vous pouvez construire la requête suivante si vous voulez juste filtrer 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; 

et si vous souhaitez filtrer à la fois first_name et 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; 

et jamais cas y vous exécuterait avec

open c for query using filterFirstName, filterLastName; 

Il est important pour la performance d'utiliser le where rownum > 0 avec DUAL comme force d'Oracle à « matérialiser » la sous-requête. Cela empêche généralement DUAL d'interférer avec le reste de la requête. Quoi qu'il en soit, vous devriez vérifier les plans d'exécution pour vous assurer qu'Oracle ne fait rien de mal.