Как получить функцию из таблицы?
Создал пробную (демо) функцию для сложения двух чисел:
create or replace FUNCTION Addition (x number, y number) RETURN number AS
BEGIN
RETURN (x+y);
END;
/
Далее, допустим есть такая таблица с данными:
Seq Inputl lnput2 Function
--- ------ ------ ----------------
A 1 2 Addition
B 3 4 Subtraction
C 5 6 Addition
D 7 8 Addition
E 9 10 Addition
Хотел бы вызывать функции указанные в таблице в PL/SQL примерно так:
declare
type datarows is table of tab1%rowtype;
data datarows;
result number;
begin
select *
bulk collect into data
from tab1 order by Seq asc;
for i in 1 .. data.count loop
result := data(i).function(data(i).Input1, data(i).Input2);
end loop;
end;
/
PLS-00224: object 'DATA(I).FUNCTION' must be of type function or array to be used this way
Понятно, что так нельзя вызвать функцию.
Есть ли какой-то другой путь решения?
Свободный перевод вопроса Derive function from table in PLSQL от участника @Deepak Sharma
Ответы (2 шт):
Mожно создать функцию, которая примет как параметр имя операции из таблицы, например, Addition или Subtraction:
create or replace function doOperation (operation varchar2, x number, y number ) return number as
begin
return case initcap (operation)
when 'Addition' then x + y
when 'Subtraction' then x - y
end;
end doOperation;
/
with t (seq, input1, input2, operation) as (
select 'A', 1, 2, 'Addition' from dual union all
select 'C', 5, 6, 'Subtraction' from dual)
select t.*, doOperation (operation, input1, input2) as result
from t;
Результат:
SEQ INPUT1 INPUT2 OPERATION RESULT
--- ---------- ---------- ----------- ----------
A 1 2 Addition 3
B 3 4 Addition 7
C 5 6 Subtraction -1
Свободный перевод ответа от участника @William Robertson
Выглядит так, что результат должен быть вычислен только после fetch, иначе виртуальных колонок было бы достаточно.
Можно просто сохранить все колонки и операции с ними как объекты, т.е воспользоваться обьектно-реляционной моделью данных:
create table tab1 of operation
/
insert into tab1 values (addition ('A', 1, 2));
insert into tab1 values (subtraction ('B', 4, 3));
commit;
Окончательный PL/SQL блок будет выглядеть почти идентично фрагменту кода из вопроса:
declare
type arr is table of operation;
data arr;
begin
select value (t)
bulk collect into data from tab1 t order by seq asc;
for i in 1..data.count loop
if data(i) is of (addition) then
dbms_output.put_line ('Result of addition '||data(i).eval);
elsif data(i) is of (subtraction) then
dbms_output.put_line ('Result of subtraction '||data(i).eval);
end if;
end loop;
end;
/
Result of addition 3
Result of subtraction 1
Создание используемых в примере объектов:
create or replace type operation force is object (
seq varchar2 (3),
member function eval return number
) not final not instantiable
/
create or replace type bioperation under operation (
arg1 number, arg2 number,
constructor function bioperation (arg1 number, arg2 number) return self as result
) not final not instantiable
/
create or replace type body bioperation is
constructor function bioperation (arg1 number, arg2 number) return self as result is
begin
self.arg1 := arg1; self.arg2 := arg2;
return;
end;
end;
/
create or replace type addition under bioperation (
overriding member function eval return number
) final
/
create or replace type body addition is
overriding member function eval return number is
begin return (arg1 + arg2);
end;
end;
/
create or replace type subtraction under bioperation (
overriding member function eval return number
) final
/
create or replace type body subtraction as
overriding member function eval return number is
begin return (arg1 - arg2);
end;
end;
/
Заметки на полях
Такой подход для простых задач требует больше кода, выигрыш станет впервые заметен, если кол-во и сложность операций значительно возрастёт. Так что, для простого примера как в вопросе предложенное решение в этом ответе, более чем достаточно. В общем случае, проще написать "грязное" решение с динамическим SQL, чем "чистое" объектно-ориентированное решение.