从 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 并丢失你的数据。)性能可能会很糟糕。

我从来没有使用过这样的系统,也不讨厌它。构建“脏”动态代码解决方案通常比构建“纯”对象关系解决方案更好。但是,如果您小心并记录所有内容,这种系统可能会有很好的用途。