【发布时间】:2018-12-10 01:39:58
【问题描述】:
我一直对我对周期性出现的一个问题缺乏了解感到困扰:功能确定性。
从docs看来,似乎还算清楚:
DETERMINISTIC 函数可能没有副作用。
DETERMINISTIC 函数可能不会引发未处理的异常。
由于这些是重要的核心概念,在标准包中具有健壮、集中的实现,我认为不存在错误或任何东西(错误在于我的假设和理解,而不是 Oracle)。话虽如此,这两个要求有时似乎在标准包以及 DBMS_ 和 UTL_ 包中都有一些特殊用途。
我希望发布几个 Oracle 函数示例,这些示例在我使用 DETERMINISTIC 以及这些限制中的细微差别时对我提出了一些疑问,看看是否有人可以解释它们是如何组合在一起的。我很抱歉这是一个“为什么”的问题,如果需要可以迁移,但对这个问题的回答:(Is it ok to ask a question where you've found a solution but don't know why something was behaving the way it was?) 让我认为它可能适合 SO。
在我的编码过程中,我经常不确定自己的 UDF 是否符合纯粹的条件,而在其他时候,我会使用令我惊讶的 Oracle 函数来发现它们是不纯的。如果有人可以看看并提供建议,我将不胜感激。
作为第一个示例,TO_NUMBER。这个函数看起来很纯粹,但它也会抛出异常。在这个例子中,我将在一个虚拟列中使用TO_NUMBER(这里应该需要DETERMINISTIC)
CREATE TABLE TO_NUMBER_IS_PURE_BUT_THROWS (
SOURCE_TEXT CHARACTER VARYING(5 CHAR) ,
NUMERICIZATION NUMBER(5 , 0) GENERATED ALWAYS AS (TO_NUMBER(SOURCE_TEXT , '99999')) ,
CONSTRAINT POSITIVE_NUMBER CHECK (NUMERICIZATION >= 0)
);
Table TO_NUMBER_IS_PURE_BUT_THROWS created.
INSERT INTO TO_NUMBER_IS_PURE_BUT_THROWS VALUES ('0',DEFAULT);
INSERT INTO TO_NUMBER_IS_PURE_BUT_THROWS VALUES ('88088',DEFAULT);
INSERT INTO TO_NUMBER_IS_PURE_BUT_THROWS VALUES ('UH-OH',DEFAULT);
1 row inserted.
1 row inserted.
ORA-01722: invalid number
ORA-01722 似乎违反了未处理异常的要求。大概我创建的任何通过TO_NUMBER 强制转换的函数都应该处理这种可能性以保持纯净。但是在这里抛出异常似乎是合适且可靠的。关于异常是否违反引用透明性似乎存在一些争论 (Why is the raising of an exception a side effect?)
我遇到的第二种情况是系统函数,它们看起来应该 DETERMINISTIC 但不是。他们被认为是不纯的肯定有一些原因。在某些情况下,内部结构会产生副作用似乎是深不可测的。
这方面的一个极端例子可能是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)
);
产量:
ORA-30553: The function is not deterministic
大概它违反了确定性的要求,但这很难想象。我想知道我在假设这样的函数是确定性的时遗漏了什么。
编辑回应 Lukasz 关于会话设置的评论:
如果跨会话可重复性是 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
【问题讨论】:
-
我猜
DBMS_ASSERT.NOOP可能因为strDBMS_ASSERT.NOOP (str VARCHAR2 CHARACTER SET ANY_CS)不纯,它可能会因会话NLS_ 参数值而异。简而言之:执行之间的字符集可能不同。至于来自 Oracle12cR2 的TO_NUMBER有DEFAULT ON CONVERSION ERROR子句。 -
谢谢卢卡斯。我也想知道会话设置——日期格式、编码等,我可以接受这是确定性的要求,但似乎一些纯函数也响应 nls/会话设置。我将更新
to_char的另一个示例 -
在我看来,这归结为实用主义。一个函数可以被标记为确定性,即使它延伸(甚至破坏)规则,只要它足够有用,值得潜在的风险。使用 to_number 风险很低并且非常有用。有了 noop,风险可能会更低,但好处在哪里?
标签: oracle plsql deterministic