Есть несколько приемов, предотвращающих ненужное повторное выполнение Oracle. Эта тема сложна, потому что 99,9% времени мы зависим от Oracle, чтобы автоматически переписать запросы для оптимальной работы. Остановка этих оптимизаций не должна быть обычной задачей.
Теоретически нет способа гарантировать порядок операций декларативного оператора SQL. На практике есть два простых метода, которые могут помочь предотвратить повторный запуск функций: скалярное кэширование подзапроса и ROWNUM
.
Во-первых, позвольте мне попытаться воспроизвести проблему. Ссылка на одно значение возвращает три разных числа.
create or replace function function_invocation return number is
begin
return dbms_random.value;
end;
/
SELECT RESULT value1,
RESULT value2,
RESULT value3
FROM (SELECT function_invocation() RESULT
FROM dual);
VALUE1 VALUE2 VALUE3
------ ------ ------
0.3089 0.7103 0.2885
Переписывание запроса для использования скалярного подзапроса кажется ненужным, но эта форма позволяет Oracle использовать скалярное кеширование подзапроса, метод оптимизации, который Oracle использует, чтобы избежать повторного выполнения кода. Теперь три столбца возвращают одно и то же значение.
select result value1, result value1, result value1
from
(
select (select function_invocation() from dual) result from dual
);
VALUE1 VALUE2 VALUE3
------ ------ ------
0.2450 0.2450 0.2450
Кроме того, мы можем предотвратить оптимизационные преобразования, добавив псевдостолбец ROWNUM
:
SELECT RESULT value1,
RESULT value2,
RESULT value3
FROM (SELECT function_invocation() RESULT, rownum
FROM dual);
VALUE1 VALUE2 VALUE3
------ ------ ------
0.1678 0.1678 0.1678
Эти методы хорошо работают на практике, чтобы результаты выглядели правильно. Однако возможно, что функция будет тайно запущена за дополнительное время до ее выполнения. Этот дополнительный прогон предназначен для анализа и кэширования и не повлияет на результаты. Но если у вас есть функция с побочными эффектами, которую можно запустить только один раз, есть некоторые дополнительные обручи, которые вы должны перепрыгнуть.