【问题标题】:INSERT statement in PL/SQL FUNCTIONPL/SQL FUNCTION 中的 INSERT 语句
【发布时间】:2017-01-17 10:24:41
【问题描述】:

我在 PL/SQL 中编写了一个函数,它使用一些参数调用 Web 服务,解析答案并返回一个值。效果很好。

但是,有时响应可能会很慢。由于参数通常位于所有可能值的一个很小的子集中,因此我想到了将答案缓存在表格中。 该函数如下所示

CREATE OR REPLACE FUNCTION myfct(p1 IN VARCHAR2, p2 IN VARCHAR2)
RETURN VARCHAR2
IS
    cache_hit NUMBER ;
    res VARCHAR2(200) ;
BEGIN
    SELECT COUNT(*) INTO cache_hit FROM MYCACHE WHERE param1 = p1 AND param2 = p2 ;

    IF( cache_hit = 1 )
    THEN
        SELECT MYCACHE.result INTO res FROM MYCACHE WHERE param1 = p1 AND param2 = p2 ;

        RETURN res ;
    END IF ;

    -- complex operations 
    res := p1 || p2 ;


    INSERT INTO MYCACHE(param1, param2, result) VALUES(p1, p2, res) ;

    RETURN res ;
END ;

当我尝试这个功能时:

SELECT myfct('ABC', 'DEF') FROM DUAL ;

我得到了错误:

ORA-14551: cannot perform a DML operation inside a query 

试图将 DML 部分包装在一个过程中并在函数中调用此过程没有帮助

我找到了 PRAGMA AUTONOMOUS_TRANSACTION 和 COMMIT 的解决方法:

CREATE OR REPLACE FUNCTION myfct(p1 IN VARCHAR2, p2 IN VARCHAR2)
RETURN VARCHAR2
IS
    PRAGMA AUTONOMOUS_TRANSACTION;
    cache_hit NUMBER ;
    res VARCHAR2(200) ;
BEGIN
    SELECT COUNT(*) INTO cache_hit FROM MYCACHE WHERE param1 = p1 AND param2 = p2 ;

    IF( cache_hit = 1 )
    THEN
        SELECT MYCACHE.result INTO res FROM MYCACHE WHERE param1 = p1 AND param2 = p2 ;

        RETURN res ;
    END IF ;

    -- complex operations 
    res := p1 || p2 ;


    INSERT INTO MYCACHE(param1, param2, result) VALUES(p1, p2, res) ;

    COMMIT ;
    RETURN res ;
END ;

但我想知道这是否真的是个好主意。提到这种解决方法的人说这可能很危险,但没有说明具体原因。

我的函数是 PRAGMA AUTONOMOUS_TRANSACTION 的一个很好用的例子,还是有更好更安全的方法来做我想做的事?

【问题讨论】:

  • 如果您在 PL/SQL 中将 select 与您的函数一起使用可能会更好地将结果保存到某个全局集合中,然后使用 forall 将值插入表中。
  • 我考虑过,但是对于所有可能的组合,我们将有几百万行要存储,并且我们计划定期仅使用约 10 000 个组合。这种带有缓存表的混合解决方案允许我们回答任何组合,但只存储最相关的组合。
  • 是否可以使结果保持最新?如果您的复杂选择的基础数据发生变化,会发生什么?也许您可以通过materialized view 解决您的性能问题?

标签: oracle function plsql


【解决方案1】:

可以从 SQL 上下文调用哪种 PL/SQL 函数是有限制的。不要在 SQL 上下文中调用您的 PL/SQL 函数,而是在 PL/SQL 上下文中调用,一切都应该很好:

declare
  v_foo constant varchar2(32767) := myfct('foo', 'bar');
begin
  dbmsn_output.put_line(v_foo);
end;

不过,在实现您自己的缓存之前,请考虑 Oracle 原生 PL/SQL Function Result Cache-feature:

PL/SQL 函数结果缓存机制提供了一种语言支持和系统管理的方式来将 PL/SQL 函数的结果缓存在共享全局区域 (SGA) 中,该区域可供运行应用程序的每个会话使用。缓存机制既高效又易于使用,减轻您设计和开发自己的缓存和缓存管理策略的负担。

当调用结果缓存函数时,系统会检查缓存。如果缓存包含先前调用具有相同参数值的函数的结果,则系统将缓存的结果返回给调用者并且不重新执行函数体。如果缓存中不包含结果,则系统运行函数体并将结果(对于这些参数值)添加到缓存中,然后将控制权返回给调用者。

【讨论】:

    【解决方案2】:

    按照惯例和良好的软件设计,函数不应更改数据库,而应仅返回值。请改用程序。这就是为什么 PLSQL 和 C 中的函数两者都有。当然,这主要被许多人认为是一种风格上的东西。但我强烈建议您将它们的用法分开。

    【讨论】:

    • 我可以同意你的观点,但我们都错了。如果您需要“记录”对函数的每次调用怎么办?如果您需要返回一个包含插入/更新/删除记录数的 json 怎么办?有很多其他数据库/语言允许这种操作(例如 postgres),我唯一的解释是 Oracle 在实现 dml 的函数中遇到了一些困难,这些函数带有良好实践的“故事”。他们应该灵活,让开发人员决定什么是或不被认为是“好”的做法。
    【解决方案3】:

    只需在匿名块中调用该函数。不要选择它。 像这样

    Declare
    V_result varchar2(20);
    Begin
    V_result := myfct('ABC', 'DEF');
    DBMS_OUTPUT.PUT_LINE(V_RESULT);
    End;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多