Oracle:在游标声明中使用自定义函数

Posted

技术标签:

【中文标题】Oracle:在游标声明中使用自定义函数【英文标题】:Oracle: Use of a custom function inside a cursor declaration 【发布时间】:2021-03-20 00:13:12 【问题描述】:

在一个过程中,我声明了一个游标。在游标声明中,我使用了datum_naar_dagdeel 函数。该函数本身确定星期几加上一天中的部分时间,并将其转换为整数。见代码摘要:

...
CURSOR c_teamvoorkeuren
IS
SELECT DISTINCT
       a.seq_afpraak
     , a.datum_start
     , t.code
     , ks.code
     , ks.naam
FROM afspraak a
   , team t
   , kamer_selectie ks
WHERE a.seq_aga_afspraak = p_seq_aga_afspraak
  AND t.seq_team = a.seq_team
  AND ks.seq_team = a.seq_team
  AND ks.dagdeel = instancename.datum_naar_dagdeel(a.datum_start)
...  

到目前为止我发现了什么:

功能上的GRANT EXECUTE已经给public了。 过程中其他地方使用的其他自定义函数运行良好。 内部带有函数的 select 语句可以完美地独立运行。运行选择部分时,我得到了预期的结果。 当我用1 替换instancename.datum_naar_dagdeel(a.date_start) 时,稍后执行的for 循环运行良好。 1 是给定约会的函数的结果,我正在测试该过程。 但是,当我使用函数而不是1 时,for 循环不会执行单次迭代。因此我得出结论,导致问题的原因是游标内的函数组合。

在游标声明中使用自定义函数时,我是否正在尝试做不可能的事情?是自定义函数还是系统函数有区别吗?

另外,我怀疑我可以构建一个使用 for 循环本身内部函数的检查。并且当检查失败时,使用continue 语句进入下一个迭代步骤。但是,这种解决方案感觉并不干净。

【问题讨论】:

没有异常处理程序……至少现在还没有。我一定会尝试你的建议。所以我猜你怀疑它应该能够工作。正如我通常在 C#.Net 或 SQL Server 上编程一样,我可能需要尝试几次才能使其正常工作。考虑到欧洲的时间,可能要等到周日或周一。 一般来说光标应该可以工作。可以提供函数datum_naar_dagdeel的代码 这似乎很容易通过调试器逐步清除。大概该函数没有返回预期值。 【参考方案1】:

我试图复制你的解释;似乎没问题在这个例子中

创建一个函数(我返回一个常量),连接为mike;将execute 授予public

SQL> connect mike/lion
Connected.
SQL> create or replace function f_test
  2    return number
  3  is
  4  begin
  5    return 20;
  6  end;
  7  /

Function created.

SQL> grant execute on f_test to public;

Grant succeeded.

用户scott 将在光标的where 子句(第6 行)中使用该函数:

SQL> connect scott/tiger
Connected.
SQL> create or replace procedure p_test
  2  is
  3    cursor c1 is
  4      select deptno, ename
  5      from emp
  6      where deptno = mike.f_test;
  7  begin
  8    for c1r in c1 loop
  9      dbms_output.put_line(c1r.deptno ||' '|| c1r.ename);
 10    end loop;
 11  end;
 12  /

Procedure created.

SQL> set serveroutput on
SQL> exec p_test;
20 SMITH
20 JONES
20 SCOTT
20 ADAMS

PL/SQL procedure successfully completed.

SQL>

所以 - 是的,它有效。


如果函数返回,例如null,没有输出(当然没有):

SQL> create or replace function f_test
  2    return number
  3  is
  4  begin
  5    return null;
  6  end;
  7  /

Function created.

SQL> connect scott/tiger
Connected.
SQL> exec p_test;

PL/SQL procedure successfully completed.

SQL>

您能否分享功能代码以及示例afspraak.datum_start 值?也许我们会注意到一些你没有注意到的东西。

【讨论】:

【参考方案2】:

找到了! (……终于)

显然,星期几的编号可能会有所不同,具体取决于会话设置。正如阅读本文的人现在可能已经猜到的那样,我的开发环境与生产环境具有不同的默认设置。我怀疑这与nls_language 设置有关。

因此,代码尝试匹配周日不存在的预订,而不是“让我们与星期一的预订设置匹配”。

只有星期一的测试数据并且不熟悉 Oracle,可能也没有帮助。我确实对今天下午再次开始调试的每个人的反馈感到鼓舞,并感谢您的帮助。

对于那些仍然感兴趣的人,我在下面添加了函数的原始代码。在当前版本中,我更改了工作日编号,从星期一的1 开始。

CREATE OR REPLACE 
FUNCTION instancename.datum_naar_dagdeel(datumtijd DATE)
  RETURN NUMBER
IS
  weekdag NUMBER := 0;
  dagdeel NUMBER := 0;
BEGIN
  
  SELECT TO_CHAR(datumtijd, 'D') INTO weekdag FROM dual;
  IF (weekdag = 2) THEN dagdeel := dagdeel + 1;  END IF;  -- Maandagochtend
  IF (weekdag = 3) THEN dagdeel := dagdeel + 3;  END IF;  -- Disndagochtend
  IF (weekdag = 4) THEN dagdeel := dagdeel + 5;  END IF;  -- Woensdagochtend
  IF (weekdag = 5) THEN dagdeel := dagdeel + 7;  END IF;  -- Donderdagochtend
  IF (weekdag = 6) THEN dagdeel := dagdeel + 9;  END IF;  -- Vrijdagochtend
  IF (weekdag = 7) THEN dagdeel := dagdeel + 11; END IF;  -- Zaterdagochtend
  
  -- Als de afspraak in de middag is, dan +1
  IF (TO_NUMBER(TO_CHAR(datumtijd, 'HH24')) >= 12) 
  THEN dagdeel := dagdeel + 1; 
  END IF; 
  
  RETURN dagdeel;
END;

-- Grants for Function
GRANT EXECUTE ON instancename.datum_naar_dagdeel TO PUBLIC;
GRANT DEBUG ON instancename.datum_naar_dagdeel TO PUBLIC;

注意:我在首次发布后更改了此回复。我的最终发现肯定与最初的问题不符。

【讨论】:

以上是关于Oracle:在游标声明中使用自定义函数的主要内容,如果未能解决你的问题,请参考以下文章

oracle异常(-)

sqlserver自定义函数里面 怎么循环查询多条结果集

oracle cursor 用法总结

oracle 自定义函数

zbb20170601 oracle PL/SQL 语句块 游标 自定义游标 异常处理EXCEPTION

开工第二天