Oracle决定论需求和特性

Oracle决定论需求和特性,oracle,plsql,deterministic,Oracle,Plsql,Deterministic,我一直为我对一个周期性出现的问题缺乏理解而困扰:函数确定性 从表面上看,这似乎相当清楚: 确定性函数可能没有副作用 确定性函数可能不会引发未处理的异常 由于这些都是重要的核心概念,在标准包中实现了健壮、集中的功能,因此我不认为存在bug或任何东西(错误在于我的假设和理解,而不是Oracle)。也就是说,这两种需求有时在标准包以及DBMS_uuu和UTL_uuu包中似乎有一些特殊用途 我希望发布一些Oracle函数的示例,这些示例在我使用确定性时会给我带来一些疑问,以及这些限制中的细微差别,看看是

我一直为我对一个周期性出现的问题缺乏理解而困扰:函数确定性

从表面上看,这似乎相当清楚:

确定性函数可能没有副作用

确定性函数可能不会引发未处理的异常

由于这些都是重要的核心概念,在标准包中实现了健壮、集中的功能,因此我不认为存在bug或任何东西(错误在于我的假设和理解,而不是Oracle)。也就是说,这两种需求有时在标准包以及DBMS_uuu和UTL_uuu包中似乎有一些特殊用途

我希望发布一些Oracle函数的示例,这些示例在我使用
确定性
时会给我带来一些疑问,以及这些限制中的细微差别,看看是否有人能解释事物是如何结合在一起的。很抱歉,这是一个“为什么”的问题,如果需要可以迁移,但是对这个问题的回答:()让我觉得这样做可能是合适的

在我的编码过程中,我周期性地面临着我自己的UDF是否符合纯标准的不确定性,而在其他时候,我使用的Oracle函数让我大吃一惊,因为它们是不纯的。如果有人能看一看并提出建议,我将不胜感激

作为第一个示例,
到_NUMBER
。这个函数看起来很纯,但也会抛出异常。在本例中,我将在虚拟列中使用
TO_NUMBER
DETERMINISTIC
这里应该是必需的)

ORA-01722似乎违反了未处理的异常要求。我所创建的任何通过
转换为_NUMBER
的函数都应该处理这种保持纯净的可能性。但是在这里抛出异常似乎是合适的、可靠的。关于异常是否违反引用透明性,似乎存在一些争论()

我遇到的第二种情况是系统函数,它们似乎应该是确定性的,而不是确定性的。他们被认为是不纯洁的,一定有什么原因。在某些情况下,内部会产生副作用,这似乎是难以理解的

一个极端的例子可能是DBMS\u ASSERT.NOOP,尽管还有很多其他例子。函数返回未修改的输入。它怎么可能是不确定的

CREATE TABLE HOW_IS_NOOP_IMPURE (
  SOURCE_TEXT VARCHAR2(256 BYTE),
  COPY_TEXT VARCHAR2(256 BYTE) GENERATED ALWAYS AS (DBMS_ASSERT.NOOP(SOURCE_TEXT)),
  CONSTRAINT COPY_IS_NOT_NULL CHECK(COPY_TEXT IS NOT NULL)
);
收益率:

ORA-30553: The function is not deterministic
它可能违反了确定性的要求,但这很难想象。我想知道我的假设中遗漏了什么,像这样的函数是确定性的

针对卢卡斯关于会话设置的评论进行编辑:
如果跨会话可重复性是像
NOOP
这样的函数不是
DETERMINISTIC
的根本原因,但TO_CHAR是确定性的/适合在虚拟列等中使用,那么我可以接受。但它似乎对其格式掩码中的会话设置很敏感:

ALTER SESSION SET NLS_NUMERIC_CHARACTERS = '._';
Session altered.

CREATE TABLE TO_CHAR_NLS(
  INPUT_NUMBER NUMBER(6,0),
  OUTPUT_TEXT CHARACTER VARYING(64 CHAR) GENERATED ALWAYS AS (TO_CHAR(INPUT_NUMBER,'999G999'))
);
Table TO_CHAR_NLS created.

INSERT INTO TO_CHAR_NLS VALUES (123456,DEFAULT);
INSERT INTO TO_CHAR_NLS VALUES (111222,DEFAULT);
SELECT INPUT_NUMBER, OUTPUT_TEXT FROM TO_CHAR_NLS ORDER BY 1 ASC;

1 row inserted.
1 row inserted.
  INPUT_NUMBER OUTPUT_TEXT
        111222  111_222
        123456  123_456
ORA-01722似乎违反了未处理的异常 要求大概我创建的任何函数都可以通过 我们应该处理这种可能性,保持纯洁

首先,我必须感谢你问了这么好的问题。现在,当您说您使用了
到_NUMBER
,它应该转换所有输入到函数的文本,但是您应该知道
到_NUMBER
有一些限制。 根据
至\u编号
定义:

TO_NUMBER
函数用于转换格式化文本或NTEXT表达式 到一个数字。此函数通常用于转换 一个应用程序的格式化数字输出(包括货币符号、十进制标记、千组标记等) 第四)以便将其用作其他应用程序的输入

它清楚地表明,它用于将一个应用程序的
格式的数字输出转换为
,这意味着
本身需要一个数字输入,当您编写以下代码时:

INSERT INTO TO_NUMBER_IS_PURE_BUT_THROWS VALUES ('UH-OH',DEFAULT);
CREATE TABLE det_test AS
SELECT TO_DATE('01-JUL-2009', 'DD-MON-YYYY') AS date_value 
FROM   dual;

SELECT date_value, SYSDATE, years_from_today(date_value)
FROM   det_test
WHERE  years_from_today(date_value) < 2;
您将意外输入完全传递给了
函数,因此它会按照预期的行为抛出错误
ORA-01722:invalid NUMBER

阅读更多关于

第二,

一个极端的例子可能是DBMS_ASSERT.NOOP,尽管有 还有很多。函数返回未修改的输入。怎么可能呢 不确定性

CREATE TABLE HOW_IS_NOOP_IMPURE (
  SOURCE_TEXT VARCHAR2(256 BYTE),
  COPY_TEXT VARCHAR2(256 BYTE) GENERATED ALWAYS AS (DBMS_ASSERT.NOOP(SOURCE_TEXT)),
  CONSTRAINT COPY_IS_NOT_NULL CHECK(COPY_TEXT IS NOT NULL)
);
DBMS\u ASSERT.NOOP
函数可用于有人通过变量传递实际代码,但不希望检查其是否存在SQL注入攻击的情况。 这必须是不确定的,因为它只返回我们输入到函数的内容

我向您展示了一个示例来说明为什么这必须是非确定性的

CREATE OR REPLACE FUNCTION years_from_today
 ( p_date   IN DATE )
RETURN NUMBER DETERMINISTIC IS
BEGIN
  RETURN ABS(MONTHS_BETWEEN(SYSDATE, p_date) / 12);
END years_from_today;
/
假设我创建了一个函数
years\u from\u today
作为
deterministic

CREATE OR REPLACE FUNCTION years_from_today
 ( p_date   IN DATE )
RETURN NUMBER DETERMINISTIC IS
BEGIN
  RETURN ABS(MONTHS_BETWEEN(SYSDATE, p_date) / 12);
END years_from_today;
/
现在,我创建一个表,并在查询中使用此函数,如下所示:

INSERT INTO TO_NUMBER_IS_PURE_BUT_THROWS VALUES ('UH-OH',DEFAULT);
CREATE TABLE det_test AS
SELECT TO_DATE('01-JUL-2009', 'DD-MON-YYYY') AS date_value 
FROM   dual;

SELECT date_value, SYSDATE, years_from_today(date_value)
FROM   det_test
WHERE  years_from_today(date_value) < 2;
然后,我在新表上创建一个基于函数的索引

CREATE INDEX det_test_fbi ON det_test (years_from_today(date_value));
现在,要了解我们的
确定性
选择的含义,请更改服务器上的日期(当然是在测试环境中),以提前一整年。即使日期已更改,再次运行查询仍将返回与以前相同的值(从今天开始算起)
,以及同一行,因为使用索引而不是执行函数

SELECT date_value, SYSDATE, years_from_today(date_value)
FROM   det_test
WHERE  years_from_today(date_value) < 2;
如果没有
WHERE
子句,查询应返回以下内容:

DATE_VALU SYSDATE   YEARS_FROM_TODAY(DATE_VALUE)
--------- --------- ----------------------------
01-JUL-09 20-SEP-11                   2.21867063
从错误的输出中可以明显看出,
函数
不应被创建为确定性函数,除非它在给定相同参数的情况下总是返回相同的值


因此,您的假设使
DBMS\u ASSERT.NOOP
在所有情况下都不成立。

我猜
DBMS\u ASSERT.NOOP
可能是不纯净的,因为str
DBMS\u ASSERT.NOOP(str VARCHAR2字符集ANY\u CS)
可能会根据会话NLS\u参数值而有所不同。简而言之:ch