2010-08-12 4 views
4

Dans notre base de données Oracle, nous avons une table appelée RÈGLES, avec un champ appelé SQLQUERY. Ce champ est un varchar avec une instruction SQL stockée. Le PK est DM_PROJECT.Comment puis-je utiliser une instruction SQL stockée dans une table dans le cadre d'une autre instruction?

Une déclaration typique qui est stockée pourrait être

select ACCOUNTNUMBER from CUSTOMERS where ACCUMULATED_SALES > 500000 

Je veux faire quelque chose comme ceci:

select 
    * 
from 
    customers 
where 
    accountnumber like 'A%' 
    or salesregion = 999 
    or accountnumber in 
    (
     <run the query SQLQUERY from RULES where DM_PROJECT=:DM_PROJECT> 
    ) 

peut-il être fait?

(préoccupation secondaire: Peut-on faire si la requête stockée utilise ses propres variables, comme

select ACCOUNTNUMBER from CUSTOMERS where ACCUMULATEDSALES > :LIMIT 

)

+0

Question intéressante! – Flukey

+1

L'effet de plateforme intérieure monte à nouveau! –

Répondre

8

Question intéressante en effet. Il y a deux aspects à cela.

La première est de savoir si c'est une bonne idée. Le problème est que le texte de la règle est invisible pour la base de données. Il n'apparaîtra pas dans les contrôles de dépendance, de sorte que l'analyse d'impact devient difficile. Évidemment (ou peut-être pas de manière évidente), la syntaxe de la Règle ne peut être vérifiée qu'en l'exécutant. Cela peut créer des problèmes avec l'ajout de règles. Cela peut donc aussi être un problème de maintenance. Et, comme nous le verrons, une fois que vous allez au-delà des requêtes simplistes, il est difficile de programmer avec.

Le deuxième aspect est de savoir si c'est possible. C'est. Nous devons utiliser le SQL dynamique; La combinaison de SQL dynamique avec SQL statique est faisable mais gnarly.

create table rules (project_name varchar2(30) 
        , rule_name varchar2(30) 
        , rule_text varchar2(4000)) 
/
insert into rules 
values ('SO', 'ACC_SALES' 
     , 'select ACCOUNTNUMBER from CUSTOMERS where ACCUMULATED_SALES > 500000 ') 
/
create table customers (accountnumber number(7,0) 
         , name varchar2(20) 
         , accumulated_sales number 
         , sales_region varchar2(3)) 
/
insert into customers values (111, 'ACME Industries', 450000, 'AA') 
/
insert into customers values (222, 'Tyrell Corporation', 550000, 'BB') 
/
insert into customers values (333, 'Lorax Textiles Co', 500000, 'BB') 
/

Cette fonction obtient une règle, l'exécute et renvoie un tableau numérique.

create or replace type rule_numbers as table of number 
/

create or replace function exec_numeric_rule 
    (p_pname in rules.project_name%type 
     , p_rname in rules.rule_name%type) 
    return rule_numbers 
is 
    return_value rule_numbers; 
    stmt rules.rule_text%type; 
begin 
    select rule_text into stmt 
    from rules 
    where project_name = p_pname 
    and rule_name = p_rname; 

    execute immediate stmt 
     bulk collect into return_value; 

    return return_value; 
end exec_numeric_rule; 
/

Permet de le tester.

SQL> select * from customers 
    2 where accountnumber in 
    3  (select * from table (exec_numeric_rule('SO', 'ACC_SALES'))) 
    4/

ACCOUNTNUMBER NAME     ACCUMULATED_SALES SAL 
------------- -------------------- ----------------- --- 
      222 Tyrell Corporation    550000 BB 

1 row selected. 

SQL> 

Quelle est la seule bonne réponse?

Mais nous arrivons maintenant à votre question suplétoire:

« Peut-il être fait si la requête stockée utilise ses propres variables »

Oui il peut, mais les choses commencent à obtenir un un peu plus fragile. Nouvelle règle:

insert into rules 
values ('SO', 'ACC_SALES_VAR' 
     , 'select ACCOUNTNUMBER from CUSTOMERS where ACCUMULATED_SALES > :LMT ') 
/

Nous modifions la fonction de l'appliquer:

create or replace function exec_numeric_rule 
    (p_pname in rules.project_name%type 
     , p_rname in rules.rule_name%type 
     , p_variable in number := null) 
    return rule_numbers 
is 
    return_value rule_numbers; 
    stmt rules.rule_text%type; 
begin 
    select rule_text into stmt 
    from rules 
    where project_name = p_pname 
    and rule_name = p_rname; 

    if p_variable is null then 
     execute immediate stmt 
      bulk collect into return_value; 
    else 
     execute immediate stmt 
      bulk collect into return_value 
      using p_variable;   
    end if; 

    return return_value; 
end exec_numeric_rule; 
/

croise les doigts!

SQL> select * from customers 
    2 where accountnumber in 
    3  (select * from table (exec_numeric_rule('SO', 'ACC_SALES_VAR', 480000))) 
    4/

ACCOUNTNUMBER NAME     ACCUMULATED_SALES SAL 
------------- -------------------- ----------------- --- 
      222 Tyrell Corporation    550000 BB 
      333 Lorax Textiles Co    500000 BB 

2 rows selected. 

SQL> 

D'accord, cela fonctionne toujours. Mais vous pouvez voir que les permutations ne sont pas amicales. Si vous voulez passer plus d'un argument à la règle, vous avez besoin de plus de fonctions ou d'une logique interne plus légère.Si vous voulez renvoyer des ensembles de dates ou de chaînes, vous avez besoin de plus de fonctions. Si vous voulez passer des paramètres P_VARIABLE de différents types de données, vous aurez peut-être besoin de plus de fonctions. Vous avez certainement besoin de pré-conditions de contrôle de type.

Ce qui revient à mon premier point: oui cela peut être fait, mais est-ce le souci?

+0

Réponse fantastique. Les gens comme vous sont ce qui rend stackoverflow génial. Bon travail, sire! – Flukey

+0

Je vais juste être d'accord avec Jamie ici, c'était vraiment intéressant et une excellente réponse! +1 à coup sûr – wasatz

+0

+1: Réponse très bien énoncée. Les fonctions de pipeline peuvent être plus performantes que les tables imbriquées pour ce type de procédure. – Allan

0

Vous pouvez utiliser SQL dynamique (Exécuter immédiate) ..please se référer pour plus Execute Immediate détails.

Questions connexes