我在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的一个好用例,还是有更好更安全的方式来做我想要的?
答案 0 :(得分:3)
可以从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 - 功能:
PL / SQL函数结果缓存机制提供了一种语言支持和系统管理的方法,用于在共享全局区域(SGA)中缓存PL / SQL函数的结果,该区域可用于运行应用程序的每个会话。缓存机制既高效又易于使用,减轻了设计和开发自己的缓存和缓存管理策略的负担。
调用结果缓存函数时,系统会检查缓存。如果高速缓存包含先前调用具有相同参数值的函数的结果,则系统将高速缓存的结果返回给调用者,并且不重新执行函数体。如果缓存不包含结果,系统将运行函数体并将结果(对于这些参数值)添加到缓存中,然后再将控制权返回给调用者。
答案 1 :(得分:0)
按照惯例和良好的软件设计,函数不应该对数据库进行更改,只应返回值。请改用程序。这就是为什么在PLSQL中只有像C中那样的函数。但是,这主要被许多人认为是一种风格。但我强烈建议你将他们的用法分开。
答案 2 :(得分:0)
只需在匿名块中调用该函数即可。不要选择它。 像这样
Declare
V_result varchar2(20);
Begin
V_result := myfct('ABC', 'DEF');
DBMS_OUTPUT.PUT_LINE(V_RESULT);
End;