SQL существует почему выбор rownum вызывает неэффективный план выполнения?

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

запрос 1:

UPDATE sales s
   SET status = 'DONE', trandate = sysdate
 WHERE EXISTS (Select *    
 FROM tempTable tmp
     WHERE s.key1 = tmp.key1
       AND s.key2 = tmp.key2
       AND s.key3 = tmp.key3)

запрос 2:

UPDATE sales s
   SET status = 'DONE', trandate = sysdate
 WHERE EXISTS (Select rownum    
 FROM tempTable tmp
     WHERE s.key1 = tmp.key1
       AND s.key2 = tmp.key2
       AND s.key3 = tmp.key3)

Как вы можете видеть, единственное различие между ними заключается в том, что подзапрос в запросе 2 возвращает rownum вместо значений каждой строки.

планы выполнения для этих двух не может быть более разные:

  • Query1-извлекает общие результаты из обеих таблиц и использует сортировку и hashjoin для возврата результатов. Это хорошо сочетается с благоприятной стоимостью 2,346 (несмотря на использование предложения EXISTS и Связного подзапроса).

  • Query2-тянет оба результата таблицы, а также, но использует счетчик и фильтр для выполнения той же задачи и возвращает план выполнения с удивительной стоимостью 77,789,696! Я должен отметить, что его запрос просто висит на мне, поэтому я не уверен, что это возвращает те же результаты (хотя я считаю, что это должно).

из моего понимания предложения Exists это просто простая логическая проверка, которая выполняется в строке главной таблицы. Не имеет значения, возвращается ли одна строка в моем состоянии EXISTS или 100 000 строк... если какие-либо результаты возвращаются для строки, которая выполняется, то вы прошли проверку exist. Так почему это имеет значение, что мой подзапрос SELECT оператор возвращается?

--------------------изменить----------------------

по запросу ниже приведены планы выполнения, которые я запускаю в TOAD... обратите внимание, что я отредактировал имена таблиц в моем примере выше для удобства - в этих планах ALSS_SALES2 = sales выше и SALESEXT_TMP = tempTABLE выше.

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

Спасибо за помощь всем!

Запрос 1 План Выполнения

Query1 Execution Plan

Запрос 2 План Выполнения

Query2 Execution Plan

------------------------------------------------

вопросы

1) Почему вызов rownum вызвал план исполнения изменить?

2) Что такого в фильтре, который настолько невероятно неэффективен?

3) я упускаю что-то фундаментальное с тем, как работает предложение Exists, которое вызывает это изменение?

2 ответов


размещение фактических планов запросов было бы весьма полезно.

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

иногда, вы увидите, что кто-то примет запрос

SELECT b.*
  FROM (SELECT <<columns>>
          FROM driving_table
         WHERE <<conditions>>) a,
       b
 WHERE a.id = b.id

и вдогонку rownum до a подзапрос

SELECT b.*
  FROM (SELECT <<columns>>, rownum
          FROM driving_table
         WHERE <<conditions>>) a,
       b
 WHERE a.id = b.id

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

конечно, в частном случае, когда у вас есть подзапрос в WHERE EXISTS где единственное использование rownum входит в SELECT список, мы, люди, можем обнаружить, что rownum не должен препятствовать любому шагу преобразования запроса, который оптимизатор хотел бы использовать. Оптимизатор, однако, вероятно, использует более общее правило, которое гласит: это подзапросы, которые ссылаются на функцию, такую как rownum должен быть полностью выполнен (это может зависеть от точной версии Oracle и/или настроек оптимизатора). Таким образом, оптимизатор реально делает кучу дополнительной работы, потому что он недостаточно умен, чтобы признать, что rownum добавленное невозможно повлиять на результаты запроса.


просто вопрос, каков план выполнения этого запроса:

UPDATE sales s
   SET status = 'DONE', trandate = sysdate
 WHERE EXISTS (Select NULL
 FROM tempTable tmp
     WHERE s.key1 = tmp.key1
       AND s.key2 = tmp.key2
       AND s.key3 = tmp.key3);

он визуализирует то, что необходимо в EXISTS (...) выражение - на самом деле ничего! Как уже говорилось, Oracle просто нужно проверить если все возвращается, а не что возвращается в подзапросе.