从 PLSQL 中的 table 派生函数
Derive function from table in PLSQL
我创建了一个演示函数来在 PLSQL 中添加两个数字:
create or replace FUNCTION Addition(
x number,
y number
)
RETURN number
AS
result number;
BEGIN
result := x+y;
RETURN result;
END;
/
commit;
现在,我有以下table:
Seq Inputl lnput2 Function
--- ------ ------ ----------------
A 1 2 Addition
B 3 4 Subtraction
C 5 6 Addition
D 7 8 Addition
E 9 10 Addition
我必须在我的程序中使用函数,如下所述:
select * bulk collect into data from table order by Seq asc;
for i in 1 .. data.count loop
result(i) := data(i).function(data(i).Input1,data(i).Input2);
end loop;
我使用这种方式遇到错误。
还有其他方法可以实现吗?
看起来像动态 SQL。
但是,因为您只想使用两个函数(并且 - 如果这些函数的数量保持那么低),更简单的选择是使用 CASE
:
(伪代码)
result := case when function = 'Addition' then addition (input1, input2)
when function = 'Subtraction' then subtraction(input1, input2)
end;
您可以创建一个函数来接受来自您的 table 的操作名称,例如 'Addition' 或 'Subtraction':
create or replace function do_the_arith
( operation varchar2
, x number
, y number )
return number
as
begin
return
case upper(operation)
when 'ADDITION' then x + y
when 'MULTIPLICATION' then x * y
when 'SUBTRACTION' then x - y
when 'DIVISION' then x / nullif(y,0) -- to avoid zero divide
when 'POWER' then power(y,y)
when 'LOG' then log(x,y)
end;
end do_the_arith;
/
with t (seq, input1, input2, operation) as
( select 'A', 1, 2, 'Addition' from dual union all
select 'B', 3, 4, 'Multiplication' from dual union all
select 'C', 5, 6, 'Subtraction' from dual union all
select 'D', 7, 8, 'Power' from dual union all
select 'E', 9, 10, 'Log' from dual )
select t.*
, do_the_arith(operation, input1, input2) as result
from t;
SEQ INPUT1 INPUT2 OPERATION RESULT
--- ---------- ---------- -------------- ----------
A 1 2 Addition 3
B 3 4 Multiplication 12
C 5 6 Subtraction -1
D 7 8 Power 16777216
E 9 10 Log 1.04795163
同意 William Robertson 的解决方案,但是对于 DIVISION 和 LOG 函数,您需要处理异常 ORA-01476:除数等于零 和 ORA-01428:参数“0”分别超出范围。
对于DIVISION我们可以只修改除数,
CASE upper(&operation) WHEN 'DIV' THEN
x / CASE y WHEN 0 THEN 1 ELSE y END
对于LOG,你必须检查如果验证失败你想做什么。检查验证 here
以下是您问题的对象关系解决方案。最终的 PL/SQL 看起来与您的代码片段几乎相同:
declare
type data_nt is table of table1%rowtype;
data data_nt;
result number;
begin
select * bulk collect into data from table1 order by seq asc;
for i in 1 .. data.count loop
result := data(i).operation.operate(data(i).input1, data(i).input2);
dbms_output.put_line('Result: '||result);
end loop;
end;
/
DBMS_OUTPUT:
Result: 2
Result: 0
以下是创建支持对象的步骤。首先,我们需要定义一个超类。这个超类什么都不做,它只是一个容器,让子类可以一起存储。
--Rollback:
-- drop table operations;
-- drop type addition;
-- drop type operation;
create or replace type operation is object
(
--Weird PL/SQL limitation - every type must have a value, so add a dummy column.
dummy varchar2(1),
member function operate(operand1 number, operand2 number) return number,
--Custom constructor so we don't have to pass it a dummy value.
constructor function operation return self as result
)
not final;
/
create or replace type body operation is
member function operate(operand1 number, operand2 number) return number is
begin
return null;
end;
constructor function operation return self as result is begin return; end;
end;
/
接下来,我们创建实际实现数学函数的子类。
create or replace type addition under operation
(
overriding member function operate(operand1 number, operand2 number) return number,
constructor function addition return self as result
) not final;
/
create or replace type body addition is
overriding member function operate(operand1 number, operand2 number) return number is
begin
return operand1 + operand2;
end;
constructor function addition return self as result is begin return; end;
end;
/
create or replace type subtraction under operation
(
overriding member function operate(operand1 number, operand2 number) return number,
constructor function subtraction return self as result
) not final;
/
create or replace type body subtraction is
overriding member function operate(operand1 number, operand2 number) return number is
begin
return operand1 - operand2;
end;
constructor function subtraction return self as result is begin return; end;
end;
/
最后,我们创建一个 table 可以保存 OPERATION 类型以及一些其他数据。
create table table1
(
seq varchar2(10) primary key,
input1 number,
input2 number,
operation operation
);
insert into table1 values('A', 1, 1, addition());
insert into table1 values('B', 1, 1, subtraction());
commit;
缺点
这种方法需要大量代码——1500 个字符来实现“+”和“-”。您所有的 SQL 语句都会变得复杂,人们将难以读取或写入数据。许多工具不支持对象关系数据。混合代码和数据会导致一些问题——您无法在不生成错误的情况下修改类型规范,例如“ORA-02303:无法删除或替换具有类型或 table 依赖项的类型”。 (而且不要直接使用“FORCE”选项。如果你强制编译类型,你会破坏 table 并丢失你的数据。)性能可能会很糟糕。
我从来没有使用过这样的系统,也不讨厌它。构建“脏”动态代码解决方案通常比构建“纯”对象关系解决方案更好。但是,如果您小心并记录所有内容,这种系统可能会有很好的用途。
我创建了一个演示函数来在 PLSQL 中添加两个数字:
create or replace FUNCTION Addition(
x number,
y number
)
RETURN number
AS
result number;
BEGIN
result := x+y;
RETURN result;
END;
/
commit;
现在,我有以下table:
Seq Inputl lnput2 Function
--- ------ ------ ----------------
A 1 2 Addition
B 3 4 Subtraction
C 5 6 Addition
D 7 8 Addition
E 9 10 Addition
我必须在我的程序中使用函数,如下所述:
select * bulk collect into data from table order by Seq asc;
for i in 1 .. data.count loop
result(i) := data(i).function(data(i).Input1,data(i).Input2);
end loop;
我使用这种方式遇到错误。
还有其他方法可以实现吗?
看起来像动态 SQL。
但是,因为您只想使用两个函数(并且 - 如果这些函数的数量保持那么低),更简单的选择是使用 CASE
:
(伪代码)
result := case when function = 'Addition' then addition (input1, input2)
when function = 'Subtraction' then subtraction(input1, input2)
end;
您可以创建一个函数来接受来自您的 table 的操作名称,例如 'Addition' 或 'Subtraction':
create or replace function do_the_arith
( operation varchar2
, x number
, y number )
return number
as
begin
return
case upper(operation)
when 'ADDITION' then x + y
when 'MULTIPLICATION' then x * y
when 'SUBTRACTION' then x - y
when 'DIVISION' then x / nullif(y,0) -- to avoid zero divide
when 'POWER' then power(y,y)
when 'LOG' then log(x,y)
end;
end do_the_arith;
/
with t (seq, input1, input2, operation) as
( select 'A', 1, 2, 'Addition' from dual union all
select 'B', 3, 4, 'Multiplication' from dual union all
select 'C', 5, 6, 'Subtraction' from dual union all
select 'D', 7, 8, 'Power' from dual union all
select 'E', 9, 10, 'Log' from dual )
select t.*
, do_the_arith(operation, input1, input2) as result
from t;
SEQ INPUT1 INPUT2 OPERATION RESULT
--- ---------- ---------- -------------- ----------
A 1 2 Addition 3
B 3 4 Multiplication 12
C 5 6 Subtraction -1
D 7 8 Power 16777216
E 9 10 Log 1.04795163
同意 William Robertson 的解决方案,但是对于 DIVISION 和 LOG 函数,您需要处理异常 ORA-01476:除数等于零 和 ORA-01428:参数“0”分别超出范围。
对于DIVISION我们可以只修改除数,
CASE upper(&operation) WHEN 'DIV' THEN
x / CASE y WHEN 0 THEN 1 ELSE y END
对于LOG,你必须检查如果验证失败你想做什么。检查验证 here
以下是您问题的对象关系解决方案。最终的 PL/SQL 看起来与您的代码片段几乎相同:
declare
type data_nt is table of table1%rowtype;
data data_nt;
result number;
begin
select * bulk collect into data from table1 order by seq asc;
for i in 1 .. data.count loop
result := data(i).operation.operate(data(i).input1, data(i).input2);
dbms_output.put_line('Result: '||result);
end loop;
end;
/
DBMS_OUTPUT:
Result: 2
Result: 0
以下是创建支持对象的步骤。首先,我们需要定义一个超类。这个超类什么都不做,它只是一个容器,让子类可以一起存储。
--Rollback:
-- drop table operations;
-- drop type addition;
-- drop type operation;
create or replace type operation is object
(
--Weird PL/SQL limitation - every type must have a value, so add a dummy column.
dummy varchar2(1),
member function operate(operand1 number, operand2 number) return number,
--Custom constructor so we don't have to pass it a dummy value.
constructor function operation return self as result
)
not final;
/
create or replace type body operation is
member function operate(operand1 number, operand2 number) return number is
begin
return null;
end;
constructor function operation return self as result is begin return; end;
end;
/
接下来,我们创建实际实现数学函数的子类。
create or replace type addition under operation
(
overriding member function operate(operand1 number, operand2 number) return number,
constructor function addition return self as result
) not final;
/
create or replace type body addition is
overriding member function operate(operand1 number, operand2 number) return number is
begin
return operand1 + operand2;
end;
constructor function addition return self as result is begin return; end;
end;
/
create or replace type subtraction under operation
(
overriding member function operate(operand1 number, operand2 number) return number,
constructor function subtraction return self as result
) not final;
/
create or replace type body subtraction is
overriding member function operate(operand1 number, operand2 number) return number is
begin
return operand1 - operand2;
end;
constructor function subtraction return self as result is begin return; end;
end;
/
最后,我们创建一个 table 可以保存 OPERATION 类型以及一些其他数据。
create table table1
(
seq varchar2(10) primary key,
input1 number,
input2 number,
operation operation
);
insert into table1 values('A', 1, 1, addition());
insert into table1 values('B', 1, 1, subtraction());
commit;
缺点
这种方法需要大量代码——1500 个字符来实现“+”和“-”。您所有的 SQL 语句都会变得复杂,人们将难以读取或写入数据。许多工具不支持对象关系数据。混合代码和数据会导致一些问题——您无法在不生成错误的情况下修改类型规范,例如“ORA-02303:无法删除或替换具有类型或 table 依赖项的类型”。 (而且不要直接使用“FORCE”选项。如果你强制编译类型,你会破坏 table 并丢失你的数据。)性能可能会很糟糕。
我从来没有使用过这样的系统,也不讨厌它。构建“脏”动态代码解决方案通常比构建“纯”对象关系解决方案更好。但是,如果您小心并记录所有内容,这种系统可能会有很好的用途。