Давайте попробуем эксперимент. Сначала запустим следующий запрос:
select lvl, rnd
from (select level as lvl from dual connect by level <= 5) a,
(select dbms_random.value() rnd from dual) b;
Подзапрос «a» вернет 5 строк со значениями от 1 до 5. Подзапрос «b» вернет одну строку со случайным значением. Если функция запускается до объединения двух таблиц (декартовой), то же самое случайное значение будет возвращено для каждой строки. Фактические результаты:
LVL RND
---------- ----------
1 .417932089
2 .963531718
3 .617016889
4 .128395638
5 .069405568
5 rows selected.
Очевидно, что функция запускалась для каждой из соединенных строк, а не для подзапроса до объединения. Это результат того, что оптимизатор Oracle решил, что лучший путь для запроса - это делать все в таком порядке. Чтобы предотвратить это, мы должны добавить что-то во второй подзапрос, чтобы Oracle полностью запустил подзапрос перед выполнением объединения. Мы добавим rownum в подзапрос, поскольку Oracle знает, что rownum изменится, если он будет запущен после объединения. Следующий запрос демонстрирует это:
select lvl, rnd from (
select level as lvl from dual connect by level <= 5) a,
(select dbms_random.value() rnd, rownum from dual) b;
Как видно из результатов, в этом случае функция была запущена только один раз:
LVL RND
---------- ----------
1 .028513902
2 .028513902
3 .028513902
4 .028513902
5 .028513902
5 rows selected.
В вашем случае кажется вероятным, что фильтр, предоставленный предложением where
, заставляет оптимизатор пойти по другому пути, когда он запускает функцию несколько раз, а не один раз. Сделав так, чтобы Oracle выполнял подзапрос в том виде, в котором он написан, вы должны получить более согласованное время выполнения.