Меня беспокоит отсутствие понимания периодически возникающей проблемы: функция-детерминированность.
Из документов кажется довольно ясным:
ДЕТЕРМИНИСТИЧЕСКАЯ функция может не иметь побочных эффектов.
ДЕТЕРМИНИСТИЧЕСКАЯ функция не может вызывать необработанное исключение.
Поскольку это важные основные концепции с надежными, централизованными реализациями в стандартных пакетах, я не думаю, что есть ошибка или что-то в этом роде (ошибка заключается в моих предположениях и понимании, а не в Oracle). При этом, как представляется, оба эти требования иногда имеют свои особенности в стандартном пакете и пакетах DBMS_ и UTL_.
Я надеялся опубликовать пару примеров функций Oracle, которые вызывают у меня некоторые сомнения в моем использовании DETERMINISTIC
и нюансах этих ограничений, и посмотреть, сможет ли кто-нибудь объяснить, как все сочетается друг с другом. Я извиняюсь, что это вопрос «почему», и его можно перенести, если это необходимо, но ответ на этот вопрос: ( Можно ли задать вопрос, когда вы нашли решение, но не знаете, почему что-то ведет себя так, как было? ) заставило меня подумать, что это может быть уместно для 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
, должна обрабатывать эту возможность, чтобы оставаться чистой. Но выбрасывание исключения здесь представляется уместным и надежным. Кажется, что есть некоторые споры о том, нарушают ли исключения прозрачность ссылок ( Почему возбуждение исключения является побочным эффектом? )
Вторая ситуация, с которой я сталкиваюсь, это системные функции, которые выглядят так, как будто они должны быть 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
Предположительно, это нарушает требования к определенности, но это трудно себе представить. Мне было интересно, что мне не хватает в моем предположении, что подобные функции будут детерминированными.
РЕДАКТИРОВАТЬ В ответ на комментарий Лукаша о настройках сессии:
Я могу принять это, если повторяемость между сессиями является основной причиной таких функций, как 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