向 Oracle 添加许多 (UDF) 验证函数 - 哪种方法运行速度最快

Posted

技术标签:

【中文标题】向 Oracle 添加许多 (UDF) 验证函数 - 哪种方法运行速度最快【英文标题】:Adding Many (UDFs) Validation Functions to Oracle - Which Method Run Fastest 【发布时间】:2014-02-19 22:29:29 【问题描述】:

我必须将大约 50 多个验证函数转移到 Oracle 中。我正在寻找运行速度最快的方法,但如果可能的话,我还想解决boolean 问题。它们的返回对象都需要相同,以便应用程序能够以一致的方式对结果做出反应并提醒用户或显示我们可能需要的任何弹出窗口、消息。我为此创建了一个valObj,但还不确定这是否是最好的方法。可以更改返回格式,因为对其做出反应的前端尚未开发。最后它将包含许多不同的验证功能,从整数、数字、电话、电子邮件、IPv4、IPv6 等......这是我目前所拥有的......

/***
This is the validation object.
It stores 1 for valid, 0 for not valid and some helper text that can be relayed back to the user.
***/
create or replace type valObj as object (
    result number(1),
    resultText varchar(32000)
);

/***
Coming from ColdFusion this seems clean to me but the function
will end up being a couple thousand lines long.
***/
create or replace function isValid(v in varchar2, format in varchar2)
return valObj
is
  test number;
begin
if format = 'number' then
    begin
        test := to_number(v);
        return valObj(1,null);
        exception when VALUE_ERROR then return valObj(0,'Invalid number. Valid formats are: 12345, 12345.67, -12345, etc...');
    end;
elsif format = 'integer' then
    null; --TO DO
elsif format = 'email' then
    null; --TO DO
elsif format = 'IPv4' then
    null; --TO DO
elsif format = 'IPv6' then
    null; --TO DO
end if;
--dozens of others to follow....
end;
/

/* Example Usage in SQL */
select isValid('blah','number') from dual; -- returns: (0, Invalid number. Valid formats are: 12345, 12345.67, -12345, etc...)
select isValid('blah','number').result from dual; -- returns: 0
select isValid('blah','number').resulttext from dual; -- returns: Valid formats are: 12345, 12345.67, -12345, etc...
select isValid(1234567890.123,'number') from dual; -- returns: 1,null
select isValid(1234567890.123,'number').result from dual; -- returns: 1
select isValid(1234567890.123,'number').resulttext from dual; -- returns: null

/* Example Usage in PL/SQL */
declare
temp valObj;
begin
    temp := isValid('blah','number');
    if (temp.result = 0) then
        dbms_output.put_line(temp.resulttext);
    else
        dbms_output.put_line('Valid');
    end if;
end;
/

我的问题是:

    在 PL/SQL 中使用它时,我希望能够像这样进行boolean 检查:if (temp.result) then 但我想不出办法,因为这在 SQL 中不起作用。我应该只向valObj 添加第三个布尔属性还是有其他我不知道的方法? 这些验证函数最终可能会在大循环中被调用。知道这一点,这是完成这些验证的最有效方法吗?

如果有任何帮助,我将不胜感激。谢谢!

更新:我忘了MEMBER FUNCTIONS。感谢@Brian McGinity 提醒我。所以我想采用这种方法,因为它将type 和它的functions 封装在一起。 这种方法和单机功能会不会有速度上的差异?这会像独立函数一样编译和存储吗?

create or replace type isValid as object (
    result     number(1),
    resulttext varchar2(32000),
    constructor function isValid(v varchar, format varchar) return self as result );
/

create or replace type body isValid as
    constructor function isValid(v varchar, format varchar) return self as result as
        test number;
    begin
        if format = 'number' then
            begin
                test := to_number(v);
                self.result := 1;
                self.resulttext := null;
                return;
                exception when VALUE_ERROR then
                    self.result := 0;
                    self.resulttext := 'Invalid number. Valid formats are: 12345, 12345.67, -12345, etc...';
                    return;
            end;
        elsif format = 'phone' then
            null; --TO DO
        end if;
        --and many others...
    end;
end;
/

/* Example Usage in SQL */
select isValid('a','number') from dual;

/* Example Usage in PL/SQL */
declare
begin
    if (isValid('a','number').result = 1) then
        null;
    end if;
end;
/

测试结果:

/* Test isValid (the object member function), this took 7 seconds to run */
declare
begin
    for i in 1 .. 2000000 loop
        if (isValid('blah','number').result = 1) then
            null;
        end if;
    end loop;
end;

/* Test isValid2 (the stand-alone function), this took 16 seconds to run */
declare
begin
    for i in 1 .. 2000000 loop
        if (isValid2('blah','number').result = 1) then
            null;
        end if;
    end loop;
end;

isValidisValid2 都执行相同的代码,他们只是运行这一行 test := to_number(v); 然后如果失败则执行异常并返回结果。这似乎是一个有效的测试? Object成员函数方法其实比独立函数快???

【问题讨论】:

函数在数据库中编译,执行速度非常快,当没有TABLE引用时(没有SQL)。对于boolean,您必须指定两个操作数以进行比较,例如truefalseboolean在内存使用方面对您有益。然后,您的两种方法都与我认为的相同。即使在循环中调用,也不应该成为问题。(如果您只在内部处理常量) 谢谢。是的,它们几乎是一样的。是的,我知道如果验证函数中有 SQL(查询),它会减慢速度。我还试图让他们将PLSQL_CODE_TYPE 更改为 NATIVE:oracle-base.com/articles/11g/… “记住,本机编译会提高过程代码的速度,但对 SQL 的性能没有影响。”但是包也是在数据库中编译的,你还认为单个大函数会更快吗? 是的,函数的大小应该无关紧要,因为在第一次执行后,它会在oracle最近的内存中,除非它很少被调用。并且连续调用不会最终将函数加载到内存中,而是立即执行。还有一些叫做pinning 的对象是SGA 的。这使得 Oracle 在 SGA 中始终拥有它,即使它不经常被调用。但不建议固定到 SGA,除非你需要它! @OracleUser 我刚刚被提醒另一个选项。如果您有时间查看问题的 UPDATE 部分,想知道您的想法是什么。谢谢。 我很想看看你的结果。我不知道如何获得这两个不同的测量值,但我确实运行了两个相同的 PL/SQL 块,一个调用对象成员函数 200 万次,另一个调用独立函数。令我惊讶的是,这个物体的速度要快得多,7 秒而不是 16 秒!我会提出测试代码的问题。 【参考方案1】:

如果将独立函数设置为 DETERMINISTIC 并且数据高度重复,则该函数会快得多。在我的机器上,这个设置将运行时间从 9 秒减少到 0.1 秒。由于我不明白的原因,设置不会提高对象功能的性能。

create or replace function isValid2(v in varchar2, format in varchar2)
return valObj
deterministic --<< Hit the turbo button!
is
  test number;
begin
if format = 'number' then
    begin
        test := to_number(v);
        return valObj(1,null);
        exception when VALUE_ERROR then return valObj(0,'Invalid number. Valid formats are: 12345, 12345.67, -12345, etc...');
    end;
end if;
end;
/

【讨论】:

必须小心确定性函数,仅当可能的输入值集较小时才使用它们。 'isNumber()' 验证函数对于确定性函数来说是一个糟糕的地方,因为可能有 10,000 到 10,000,000+ 种不同的组合——oracle 如何跟踪它? @BrianMcGinity 这是个好问题,我不确定 Oracle 如何在 PL/SQL 中缓存确定性函数。根据this AskTom article Oracle 散列 255 个值用于标量子查询缓存,但我不确定相同的算法是否适用于 PL/SQL。我从未听说有人遇到过 DETERMINISTIC 函数缓存过多的问题,我认为这是有限制的。 好吧,在我的测试中,确定性肯定有很大帮助。只要我传入x 而不是'blah',它对对象中的函数也有帮助。这真的很奇怪,需要一个新问题,我从这里开始并展示了我的测试结果:***.com/q/21916218/3112803 基于这些结果,具有确定性的成员函数方式似乎最适合我的用例。你同意?这个答案引起了最大的速度提升,所以我会奖励答案,但我认为我会使用确定性和成员函数,而不是独立函数。 确定性函数专为小得多的输入和结果集而设计。它们存储在内存中并为每个用户复制。您可以将其用于男性/女性或真/假验证功能。就我个人而言,我只会在参数/结果集少于 100 个不同值的情况下使用它,因为 Oracle 会为每组输入值/结果存储一个哈希映射。有关更多信息,请参阅:allthingsoracle.com/caching-part-1 @BrianMcGinity 我认为函数结果缓存与确定性函数发生的任何事情之间存在一些混淆。我刚刚在一个具有 10 亿个唯一值的小型本地数据库上运行了一个简单的测试,确定性和非确定性都在相同的时间内运行。我不确定 Oracle 的算法是用于确定性函数的,但当不同值的数量很大时,它似乎不会使用大量资源。 DETERMINISTIC 并不总是有帮助,但只要它在逻辑上是有效的,它似乎永远不会受到伤害。【参考方案2】:

可能还需要考虑使用 pls_integer 而不是数字。不知道它是否会给你带来很多好处,但文件表明会有一些收益。 http://docs.oracle.com/cd/B10500_01/appdev.920/a96624/03_types.htm 州, “您使用 PLS_INTEGER 数据类型来存储有符号整数。它的大小范围是 -2*31 .. 2*31。PLS_INTEGER 值比 NUMBER 值需要更少的存储空间。此外,PLS_INTEGER 操作使用机器算术,所以它们比使用库算法的 NUMBER 和 BINARY_INTEGER 运算要快。为了提高效率,对所有在其幅度范围内的计算都使用 PLS_INTEGER。"

【讨论】:

这似乎是一个 PL/SQL 数据类型。所以我只能将它添加到检查它是否是数字的函数中。我无法将对象中的result number(1); 更改为PLS_INTEGER。随着功能的更改,我在 200 万次循环测试中发现时间没有变化。我仍然会使用它,因为它是有道理的,但我没有看到任何收获。谢谢!

以上是关于向 Oracle 添加许多 (UDF) 验证函数 - 哪种方法运行速度最快的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Java UDF 向 Spark 数据框添加新列

如何使用Java UDF将新列添加到Spark数据帧

向 udf pig latin 发送矩阵

hive.optimize.index.filter设置我true,tez查询udf函数报错

使用 UDF 在单元格中定义下拉列表(数据验证)

如何从函数(UDF)返回表变量?