Добавление условий избыточного соединения в Oracle приводит к другому плану - PullRequest
5 голосов
/ 14 июня 2009

У меня есть общая ситуация соединения с базой данных, включающая три таблицы. Одна таблица A является основной таблицей с первичным ключом с именем id. Таблицы B и C содержат вспомогательные данные для записей и A, и каждая из них также имеет столбец с именем id, который является внешним ключом, указывающим на A. id. Теперь, если мне нужны все данные из A, B и C в одном запросе, я бы написал:

SELECT *
FROM A
INNER JOIN B
ON B.id = A.id
INNER JOIN C
ON C.id = A.id

, что, конечно, прекрасно работает.

Недавно наш администратор базы данных сказал нам, что это неэффективно в Oracle, и вам также необходимо объединить условия между C и B следующим образом:

SELECT *
FROM A
INNER JOIN B
ON B.id = A.id
INNER JOIN C
ON C.id = A.id AND C.id = B.id

Это выглядело излишним для меня, поэтому, естественно, я не поверил здесь. Пока я фактически не столкнулся с медленным запросом, который имел ужасный план выполнения, и сумел исправить его, добавив отсутствующее условие соединения. Я выполнил план объяснения для обеих версий: тот, у которого не было «избыточного» условия запроса, стоил 1 035, а «улучшенный» - 389 (и также имелись огромные различия в количестве элементов и в байтах). Оба запроса дали одинаковый результат.

Может кто-нибудь объяснить, почему это дополнительное условие имеет значение? Для меня C и B даже не связаны. Также обратите внимание, что если вы уберете другое условие соединения, оно одинаково плохо - они оба должны быть там.

Ответы [ 4 ]

3 голосов
/ 15 июня 2009

Интересно.

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

Но чтобы быть в безопасности, лучше разберись с лишним предикатом.

2 голосов
/ 14 июня 2009

Оптимизатор Oracle не делает транзитивных предположений о равенстве. Хотя мы понимаем, что если A = B и A = C, то B = C, Oracle не предполагает наличие связи между B & C, если только она явно не указана в предложении WHERE или условиях JOIN.

Я предполагаю, что у вас есть другие ограничения на A, B и / или C (в отличие от простого выбора всего содержимого таблиц - иначе ваш ввод / вывод не был бы таким низким, если бы ваши таблицы были крошечными какая точка оптимизации несколько спорная). Так что на A, B & C действительно больше ограничений, чем указано. Оптимизатор Oracle будет просматривать все таблицы в предложении FROM, перечислять ограничения к ним в предложении WHERE, а затем определять селективность ограничений на основе индексов для этих таблиц. Затем он пройдет через различные варианты планов атаки и определит, какие из них дают наибольшую надежду (это значения мощности, которые вы видите в планах). Без условия B = C это исключит планы, начинающиеся с B и продолжающиеся до C (или наоборот), и это могут быть наилучшие возможные планы.

2 голосов
/ 15 июня 2009

У вас есть две проблемы.

Во-первых, с исходным SQL оптимизатор делает оценку количества строк в A со строками, совпадающими с идентификатором в B, которые также имеют совпадающую строку в C. Оценка является неточной, и выбирается неправильный план.

Теперь вы добавляете избыточное условие. Oracle предполагает, что никакие условия не являются действительно избыточными (как если бы они были, интеллектуальный разработчик не включил бы их). Это также предполагает, что каждое условие не зависит от других. Например, выбор, где hair = 'bald' может получить 10% от таблицы, выбор, где пол = 'F', может получить 50%. Oracle предположил бы, что выбор, где hair = 'лысый' и пол = 'F', даст 5% (тогда как в действительности облысение в основном ограничено мужчинами).

Добавляя предикат «избыточность», Oracle будет переоценивать числа или строки, которые нужно исключить, и соответственно выберет план.

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

Это не решение, которое я бы рекомендовал, но если оно работает .....

PS. Я предполагаю, что типы данных всех идентификаторов совпадают. Если B.ID и C.ID - дата, а A.ID - символ, или наоборот, то возможно иметь несколько строк, где A.ID = B.ID и A.ID = C.ID, но B.ID! = C.ID, потому что неявное преобразование может потерять метки времени.

1 голос
/ 14 июня 2009

Эти два запроса не выглядят одинаково для меня вообще.
Опять же, я не оптимизатор Oracle.

Поскольку у B и C есть внешние ключи A, когда вы сделали это

INNER JOIN B
ON B.id = A.id

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

Итак, вы оптимизировали вещи во втором запросе, присоединив таблицу C к меньшему набору данных, который является пересечением A и B, и к меньшему набору данных, который является пересечением A и С.

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