2016-10-28 3 views
3

Update 11/2Oracle 12c Подзапрос Факторинг Inline View теперь имеет плохой план?

После некоторых дополнительных поиска и устранения неисправностей, моя команда была в состоянии связать эту ошибку Oracle непосредственно к изменению параметра, который был сделан на базе 12c ночи перед запрос перестал работать. После некоторых проблем с производительностью из приложения, привязанного к этой базе данных, моя команда изменила наш DBA параметр OPTIMIZER_FEATURES_ENABLE с 12.1.02 на 11.2.0.4. Это устранило проблему производительности для проблемного приложения, но вызвало ошибку, описанную выше. Чтобы проверить, я смог воспроизвести эту же проблему в отдельной среде, изменив этот параметр. Мой DBA подал билет с Oracle, чтобы это выглядело.

В качестве обходного пути я смог внести небольшое изменение в свой запрос, чтобы получить ожидаемые результаты. В частности, я объединил Subquery1 с Subquery2, и я переместил несколько предикатов в Subquery1 из предложения WHERE в JOIN (где они более правильно принадлежали). Это изменение изменило мой план выполнения (он немного менее эффективен, чем тот, который был указан ранее), но этого было достаточно для решения исходной проблемы.


Оригинал Сообщение

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

фон

У меня есть Oracle запрос, который я положил в производство давно, что в последнее время перестали производить ожидаемые результаты по совпадению после обновления от 11g до 12c. По моему (и моей команде поддержки поддержки) этот запрос работал нормально уже более года до этого.

Деталь

Запрос является слишком сложным и не очень эффективным, но это в значительной степени потому, что я имею дело с не нормированными таблицами (исторически смоделированным после мэйнфреймов) и плохого вводом данных из добывающих систем. Чтобы справиться со сложной ситуацией в бизнесе, я использовал несколько уровней подзапроса факторинга (оператор WITH), а затем мое заключительное заявление объединяет два встроенных представления. Основная структура запроса без всех сложных предикатов выглядит следующим образом:

У меня есть 3 таблицы Table1, Table2, Table3. Table1 - таблица обработки, составленная из записей от Table2.

--This grabs a subset from Table1 
WITH Subquery1 as (
    SELECT FROM Table1), 

--This eliminates certain records from the first subset based on sister records 
--from the original source table 
Subquery2 as (
    SELECT FROM Subquery1 
    WHERE NOT EXISTS FROM (SELECT from Table2)), 

--This ties the records from Subquery2 to Table3 
Subquery3 as (
    SELECT FROM Table3 
    JOIN (SELECT Max(Date) FROM Table3) 
    JOIN Subquery2) 

--This final query evaluates subquery3 in two different ways and 
--only takes those records which fit the criteria items from both sets 
SELECT FROM 
(SELECT FROM Subquery3)    -- Call this Inline View A 
JOIN (SELECT FROM Subquery3)  -- Call this Inline View B 

окончательный запрос довольно простой:

SELECT A.Group_No, B.Sub_Group, B.Key, B.Lob    
    FROM (SELECT Group_No, Lob, COUNT(Sub_Group) 
      FROM Subquery3 
      GROUP BY Group_No, Lob 
      HAVING COUNT(Sub_Group) = 1) A 
    JOIN (SELECT Group_No, Sub_Group, Key, Lob 
     FROM Subquery3 
     WHERE Sub_Group LIKE '0000%') B 
    ON A.Group_No = B.Group_No 
    AND A.Lob = B.Lob 

Проблема

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

Аналогичным образом, если я отредактирую окончательный запрос, чтобы произвести вывод только встроенного представления «B», я ухожу с 6 возвращенных строк. Опять же, я вручную оценил данные, и это точно так, как ожидалось.

Теперь, когда мы соединяем эти два подмножества (Inline View A и Inline View B), я ожидаю, что итоговый результат запроса будет 0 строк (так как внутреннее соединение между полным набором и пустым набором не дает совпадений) , Однако, когда я запускаю весь запрос с внутренним соединением, как описано выше, Я возвращаюсь 1158 строк!

Я рассмотрел план выполнения, но ничего не выскакивает на меня:

Execution Plan 1 Execution Plan 2

Вопросы

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

До сих пор я не смог найти официальную документацию Oracle вокруг инструкции WITH, поэтому я никогда не был полностью уверен в том, что порядок подзапросов оценивается. Я заметил, что в поиске SO (не может найти его сейчас), кто-то сказал, что факторизованный подзапрос не может ссылаться на другой факторизованный запрос. Я никогда раньше не знал, что это правда, но причудливый вывод выше заставляет меня задаться вопросом, не дождался ли я раньше этого вопроса?

Может ли кто-нибудь объяснить поведение, которое я вижу? Я пытаюсь сделать что-то явно неправильное с этим планом запроса? Или, альтернативно, есть ли вероятность, что что-то изменилось между 11g и 12c, что может объяснить, почему поведение этого запроса могло измениться?

+1

Привет, - Абсолютно факторизованный подзапрос CAN может ссылаться на другой (всегда определенный перед ним), я использую это все время в довольно сложных комбинациях, и он никогда не терпит неудачу, как в 11.2, так и 12.1. Тогда - многое другое может измениться между версиями Oracle; правило, которое ранее не применялось, или они улучшают реализацию, но вы сталкиваетесь с ситуацией, когда они не тестировали, где они что-то сломали, что происходит все время. Вы можете рассмотреть возможность публикации в OTN, есть несколько экспертов по оптимизации, которые не посещают SO, если вообще. Удачи! – mathguy

+1

Или, действительно, ничего себе, теперь, когда я прочитал полный вопрос - у вас не просто проблема с эффективностью, у вас также есть логическая проблема. Это гораздо более вероятно, чтобы выявить ошибку в коде, а не Oracle что-то сломать. Поскольку вы не можете использовать код для очевидной причины защиты бизнеса, будет очень сложно получить помощь. Можете ли вы последовательно упростить запрос, подтверждая, что проблема все еще существует? Попытайтесь изолировать проблему - это может быть ошибкой в ​​коде, о котором вы ранее не знали. – mathguy

+0

@mathguy Спасибо за подтверждение о зависимых факторизованных подзапросах. Я думал, что сначала схожу с ума. Я чувствую себя более уверенно в том, что обнаружил некоторую ошибку в Oracle с тем, как я структурировал этот запрос, поэтому я собираюсь начать рефакторинг, чтобы проверить, не могу ли я его изолировать. – DanK

ответ

2

Это звучит как ошибка «неправильных результатов» в Oracle. Эти ошибки обычно чрезвычайно специфичны для версии и функций, которые вы используете. Нет ничего явно неправильного в запросах или плане выполнения, который вы опубликовали.

У вас есть два способа обработки этого:

  1. попытаться найти точную ошибку. То, что вы делаете с обычными табличными выражениями, выглядит отлично.Есть несколько редких случаев, когда ваш запрос технически недействителен, вы получаете «повезло» в одной версии, и он работает, и когда вы его обновляете, вы не можете. Но когда это происходит, новая версия обычно выдает ошибку, а не возвращает неверные результаты. Вероятно, существует какая-то чрезвычайно странная, конкретная комбинация функций, которые вы используете, что вызывает проблему. Чтобы найти реальную проблему, вам необходимо значительно упростить запрос до тех пор, пока вы не сможете сделать минимально возможное изменение и увидеть, как проблема появляется и исчезает. Вы также захотите удалить все объекты и использовать только DUAL. Этот процесс может занять несколько часов. В конце, когда вы остаетесь только с несколькими строками кода, отправьте их здесь, посмотрите на службу поддержки Oracle или создайте запрос на обслуживание.
  2. Избегайте ошибок. Даже если вы пройдете указанные шаги, в любом случае может не быть исправления. Иногда лучшая работа - это делать что-то по-другому. Приятно разобраться во всех проблемах, но у вас не всегда есть время. Вместо этого попробуйте повторно написать запрос синтаксически разными, но логически эквивалентными способами. Удалите некоторые или все общие табличные выражения, возможно, даже повторите некоторые SQL. Но не забудьте оставить комментарий, предупреждающий будущих программистов о том, почему вы делаете что-то странным образом.
+1

Спасибо, Джон. С вашими комментариями и @mathguy я чувствую себя более уверенно, что я не просто делаю что-то идиотское. Я собираюсь взять ваш совет и начать играть с запросом, чтобы узнать, не могу ли я точно определить, где все происходит неправильно. Если я выясню проблему, я отправлю ее в Oracle и опубликую здесь свои выводы. – DanK