【问题标题】:Derive function from table in PLSQL从 PLSQL 中的表导出函数
【发布时间】:2020-11-11 06:23:17
【问题描述】:

我创建了一个在 PLSQL 中添加两个数字的演示函数:

create or replace FUNCTION Addition(    
    x number,
    y number
    )     
    RETURN number
AS
    result number; 
BEGIN
    result := x+y;  
    RETURN result;  
END; 
/
commit;

现在,我有下表:

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;

我使用这种方式会出错。

还有其他方法可以实现吗?

【问题讨论】:

  • 下面使用附加参数和 CASE 语句的答案可能是此类问题的最佳解决方案。但是,您的示例代码意味着您正在寻找一个对象关系解决方案,其中具有相关功能的对象直接存储在表中。对象关系方法通常不是一个好主意,但如果您想要的话,请告诉我们,我可以创建一个示例。
  • 我个人的看法是,在创建基本计算器的这个功能中,我相信“对象关系方法”会很方便。
  • @JonHeller 是的,请。如果可能,请告诉我对象关系方法。
  • @Sujitmohanty30 我需要的不是算术方法,而是用户定义的函数。

标签: oracle function stored-procedures plsql


【解决方案1】:

以下是您问题的对象关系解决方案。最终的 PL/SQL 看起来几乎与您的代码 sn-ps 相同:

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

最后,我们创建一个可以保存 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:不能删除或替换类型或表依赖的类型”之类的错误。 (并且不要只是跳到使用“FORCE”选项。如果你强制编译类型,你会破坏表格并丢失数据。)性能可能很糟糕。

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

【讨论】:

    【解决方案2】:

    您可以创建一个函数,该函数接受表中的操作名称,例如“加法”或“减法”:

    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
    

    【讨论】:

      【解决方案3】:

      同意 William Robertson 的解决方案,但是对于 DIVISION 和 LOG 函数,您需要处理异常 ORA-01476: divisor is equal to zero 和 ORA-01428: argument '0' is out范围。

      对于 DIVISION 我们可以只修改除数,

      CASE upper(&operation) WHEN 'DIV' THEN
                    x / CASE y WHEN 0 THEN 1 ELSE y END
      

      对于 LOG,如果验证失败,您必须检查要执行的操作。检查验证here

      【讨论】:

        【解决方案4】:

        看起来像动态 SQL。

        但是,由于您只想使用两个函数(并且 - 如果这些函数的数量保持在这么低的水平),更简单的选择是使用 CASE

        (伪代码)

        result := case when function = 'Addition'    then addition   (input1, input2)
                       when function = 'Subtraction' then subtraction(input1, input2)
                  end;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-08-06
          • 2018-01-17
          • 1970-01-01
          • 1970-01-01
          • 2015-04-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多