Почему этот Oracle 10g SQL работает медленно, только когда я запрашиваю подзапрос с предложением where? - PullRequest
4 голосов
/ 16 июня 2011

Я не могу вставить весь SQL по разным причинам, поэтому рассмотрим следующий пример:

select * 
from
    (select nvl(get_quantity(1), 10) available_qty 
     from dual)
where available_qty > 30;

get_quantity - это функция, которая выполняет вычисления на основе идентификатора записи, которая прошла через нее. Если он возвращает ноль, я использую nvl(), чтобы заставить его к 10.

Запрос выполняется очень медленно, когда я использую предложение WHERE в родительском запросе. Однако, когда я комментирую предложение WHERE, оно работает очень быстро. Чего я не понимаю, так это того, почему он может отображать данные очень быстро, но не может запрашивать их так же быстро. Я также запрашиваю результаты подзапроса. У меня сложилось впечатление, что подзапросы возвращают «визуализированный» набор данных. Как будто запрос идентификатора available_qty заставляет его ссылаться на что-то в подзапросе.

Вот почему я не думаю, что содержимое функции get_quantity здесь уместно, поэтому я не стал ее публиковать. Вместо этого я думаю, что это неправильное понимание с моей стороны того, как Oracle обрабатывает подзапросы и еще много чего.

Кто-нибудь из вас, гуру Оракула, знает, что я делаю неправильно?

Запоздалая мысль: когда я вводил теги для этого вопроса, появился тег "коррелированный подзапрос". При проведении некоторых быстрых исследований кажется, что этот тип подзапроса в некоторой степени зависит от внешнего запроса. Может ли это быть связано с моей проблемой?

1 Ответ

7 голосов
/ 16 июня 2011

Давайте попробуем эксперимент. Сначала запустим следующий запрос:

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 выполнял подзапрос в том виде, в котором он написан, вы должны получить более согласованное время выполнения.

...