Альтернатива для курсора - PullRequest
5 голосов
/ 31 марта 2019

У меня есть функция, которая с каждым вызовом меняет свое значение. Я хотел бы использовать это значение более одного раза в запросе, но я не хочу выполнять его более одного раза, потому что я хочу значение из первого вызова. Я пробовал:

SELECT RESULT value1,
       RESULT value2,
       RESULT value3
  FROM (SELECT function_invocation() RESULT
          FROM dual);

Но каждый столбец VALUE дает мне различное значение, что означает, что функция была вызвана более одного раза.

Альтернативой может быть написание курсора, но мне было интересно, возможно ли это с чистым SQL.

1 Ответ

6 голосов
/ 31 марта 2019

Есть несколько приемов, предотвращающих ненужное повторное выполнение 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

Эти методы хорошо работают на практике, чтобы результаты выглядели правильно. Однако возможно, что функция будет тайно запущена за дополнительное время до ее выполнения. Этот дополнительный прогон предназначен для анализа и кэширования и не повлияет на результаты. Но если у вас есть функция с побочными эффектами, которую можно запустить только один раз, есть некоторые дополнительные обручи, которые вы должны перепрыгнуть.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...