Подтвердить что ты не робот

Oracle SQL: Понимание поведения SYS_GUID(), если оно присутствует во встроенном представлении?

Вот пример SQL, о котором идет речь; SQL должен работать на любой СУБД Oracle (я запускаю 11.2.0.2.0).

Обратите внимание на то, как значения UUID отличаются (у одного есть 898, у другого есть 899) в наборе результатов, несмотря на то, что они построены из встроенного представления/с предложением. Ниже вы можете увидеть, как DBMS_RANDOM.RANDOM() не имеет этого побочного эффекта.

SQL:

WITH data AS (SELECT SYS_GUID () uuid FROM DUAL)
    SELECT uuid, uuid
      FROM data

Вывод:

UUID                                      UUID_1
F8FCA4B4D8982B55E0440000BEA88F11      F8FCA4B4D8992B55E0440000BEA88F11

В Contrast DBMS_RANDOM результаты те же

SQL:

WITH data AS (SELECT DBMS_RANDOM.RANDOM() rand FROM DUAL)
SELECT rand, rand
  FROM data

Вывод:

RAND    RAND_1
92518726    92518726

Еще интереснее я могу изменить поведение/стабилизировать sys_guid, включив вызовы в DBMS_RANDOM.RANDOM:

WITH data AS (
        SELECT SYS_GUID () uuid, 
        DBMS_RANDOM.random () rand 
        FROM DUAL)
SELECT uuid a,
       uuid b,
       rand c,
       rand d
  FROM data

SQL Fiddle, который стабилизирует SYS_GUID: http://sqlfiddle.com/#!4/d41d8/29409

SQL Fiddle Показывает нечетное поведение SYS_GUID: http://sqlfiddle.com/#!4/d41d8/29411

4b9b3361

Ответ 1

Документация дает причину относительно того, почему вы можете увидеть расхождение (акцент мой):

Внимание:

Поскольку SQL - это декларативный язык, а не императивный (или процедурный), , вы не можете знать, сколько раз функция, вызываемая оператором SQL, будет работать - даже если функция написана в PL/SQL, императивный язык. Если вашему приложению требуется, чтобы функция выполнялась определенное количество раз, не вызывайте эту функцию из инструкции SQL. Вместо этого используйте курсор.

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

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

Однако существуют способы ограничения перезаписи запроса, как описано в главе Unnesting inested Subqueries:

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

Как объяснялось выше, вы можете использовать псевдо-столбец ROWNUM, чтобы предотвратить прерывание подзапроса Oracle:

SQL> WITH data AS (SELECT SYS_GUID() uuid FROM DUAL WHERE ROWNUM >= 1)
  2  SELECT uuid, uuid FROM data;

UUID                             UUID
-------------------------------- --------------------------------
1ADF387E847F472494A869B033C2661A 1ADF387E847F472494A869B033C2661A

Ответ 2

Совет NO_MERGE "исправляет" его. Предотвращает переписывание встроенного представления Oracle.

WITH data AS (SELECT /*+ NO_MERGE */
                    SYS_GUID () uuid FROM DUAL)
SELECT uuid, uuid
  FROM data

Из документов:

Совет NO_MERGE дает указание оптимизатору не комбинировать внешний запрос и любые встроенные запросы просмотра в один запрос. Этот совет позволяет у вас больше влияния на доступ к виду.

SQL Fiddle с подсказкой NO_MERGE:

Я все еще пытаюсь понять/сформулировать, как запрос переписывается таким образом, что sys_guid() будет вызываться дважды. Возможно, это ошибка; но я склонен предположить, что это ошибка в моих собственных мыслях/коде.

Ответ 3

Очень интересно.

Мы можем использовать подсказку материализации, чтобы ее исправить.

WITH data AS (SELECT /*+materialize*/SYS_GUID () uuid FROM DUAL)
    SELECT uuid, uuid
      FROM data;

1   F9440E2613761EC8E0431206460A934C    F9440E2613761EC8E0431206460A934C

С моей точки зрения, если мы сможем изменить результат запроса, просто добавив подсказку, появляется ошибка Oracle. Может быть, мы должны попросить металлик проверить это...