Как я могу использовать оператор SQL, хранящийся в таблице, как часть другого оператора? - PullRequest
4 голосов
/ 12 августа 2010

В нашей базе данных Oracle у нас есть таблица RULES с полем SQLQUERY. Это поле является varchar с сохраненным оператором SQL. ПК является DM_PROJECT.

Типичным сохраненным оператором может быть

select ACCOUNTNUMBER from CUSTOMERS where ACCUMULATED_SALES > 500000

Я хочу сделать что-то вроде этого:

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

Можно ли это сделать?

(Вторичная проблема: можно ли это сделать, если хранимый запрос использует свои собственные переменные, например

select ACCOUNTNUMBER from CUSTOMERS where ACCUMULATEDSALES > :LIMIT 

)

Ответы [ 2 ]

9 голосов
/ 12 августа 2010

Действительно интересный вопрос. Здесь есть два аспекта.

Во-первых, хорошая ли это идея. Проблема в том, что текст в правиле невидим для базы данных. Он не будет отображаться при проверке зависимостей, поэтому анализ влияния становится сложным. Очевидно (или, может быть, не очевидно) синтаксис правила может быть проверен только путем его запуска. Это может создать проблемы с добавлением правил. Так что это также может быть проблемой обслуживания. И, как мы увидим, как только вы выйдете за рамки упрощенных запросов, будет сложно программировать.

Второй аспект - возможно ли это? Это. Нам нужно использовать динамический SQL; объединение динамического SQL со статическим SQL выполнимо, но довольно сложно.

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')
/

Эта функция получает правило, выполняет его и возвращает числовой массив.

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;
/

Давайте проверим это.

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>

Какой единственный и правильный ответ.

Но теперь мы подходим к вашему дополнительному вопросу:

"Можно ли это сделать, если сохраненный запрос использует свои собственные переменные "

Да, может, но все становится немного более хрупким. Новое правило:

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

Мы изменим функцию, чтобы применить ее:

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;
/

Пальцы скрещены!

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>

Хорошо, все еще работает. Но вы можете видеть, что перестановки не дружелюбны. Если вы хотите передать более одного аргумента в правило, то вам нужно больше функций или более крутой внутренней логики. Если вы хотите вернуть наборы дат или строк, вам нужно больше функций. Если вы хотите передать параметры P_VARIABLE различных типов данных, вам может потребоваться больше функций. Вам, безусловно, нужны предварительные условия проверки типов.

Что возвращает меня к первому пункту: да, это можно сделать, но стоит ли это хлопот?

0 голосов
/ 12 августа 2010

Вы можете использовать динамический SQL (Немедленное выполнение). Пожалуйста, обратитесь Немедленное выполнение для более подробной информации.

...