Во-первых, причина, по которой вы получаете другое время выполнения, не в том, что Oracle часто выполняет функцию даты. Выполнение этой функции SQL, даже если она выполняется для каждой строки (вероятно, это не так), занимает незначительное количество времени по сравнению со временем, которое требуется для фактического извлечения строк из диска / памяти.
Вы получаете совершенно другое время выполнения, потому что, как вы заметили, Oracle выбирает другой путь доступа. Выбор одного пути доступа по другому может привести к разнице во времени выполнения на порядки величин. Поэтому реальный вопрос не в том, «почему add_months
требует времени?». но:
Почему Oracle выбирает этот конкретный неэффективный путь, в то время как есть более эффективный?
Чтобы ответить на этот вопрос, нужно понять, как работает оптимизатор. Оптимизатор выбирает конкретный путь доступа, оценивая стоимость нескольких путей доступа (все из них, если имеется только несколько таблиц) и выбирая план выполнения, который, как ожидается, будет наиболее эффективным. Алгоритм для определения стоимости плана выполнения имеет правила, и он делает свою оценку на основе статистики, собранной из ваших данных.
Как и все алгоритмы оценки, он делает предположения о ваших данных, такие как общее распределение, основанное на минимальном / максимальном значении столбцов, количество элементов и физическое распределение значений в сегменте (коэффициент кластеризации).
Как это относится к вашему конкретному запросу
В вашем случае оптимизатор должен оценить селективность различных предложений фильтра. В первом запросе фильтр находится между двумя переменными (add_months(trunc(sysdate,'MM'), -1) and sysdate
), в то время как в другом случае фильтр находится между константой и переменной.
Они выглядят одинаково для вас, потому что вы заменили переменную на ее значение, но для оптимизатора случаи очень разные: оптимизатор (по крайней мере, в 8i) вычисляет план выполнения только для определенного запроса. Как только путь доступа будет определен, все последующее выполнение получит тот же план выполнения. Поэтому он не может заменить переменную на ее значение, потому что это значение может измениться в будущем, и план доступа должен работать для всех возможных значений.
Поскольку во втором запросе используются переменные, оптимизатор не может точно определить селективность первого запроса, поэтому оптимизатор делает предположение, что в результате приводит к неверному плану.
Что вы можете сделать, если оптимизатор не выберет правильный план
Как упоминалось выше, оптимизатор иногда делает неверные предположения, что приводит к неоптимальному пути доступа. Даже если это случается редко, это может иметь катастрофические последствия (часы, а не секунды). Вот некоторые действия, которые вы можете попробовать:
- Убедитесь, что ваша статистика актуальна . Столбцы
last_analyzed
на ALL_TABLES
и ALL_INDEXES
сообщат вам, когда в последний раз собирали статистику по этим объектам. Хорошие достоверные статистические данные приводят к более точным догадкам, что (надеюсь) приводит к улучшению плана выполнения.
- Узнайте о различных вариантах сбора статистики (пакет
dbms_stats
)
- Перепишите ваш запрос, чтобы использовать константы, когда это имеет смысл, чтобы оптимизатор сделал более надежные предположения.
- Иногда два логически идентичных запроса приводят к разным планам выполнения, поскольку оптимизатор не будет вычислять одни и те же пути доступа (из всех возможных путей).
- Есть несколько приемов, которые вы можете использовать, чтобы заставить оптимизатор выполнить некоторые объединения раньше других, например:
- Используйте rownum для материализации подзапроса (это может занять больше временного пространства, но позволит вам выполнить оптимизатор через определенный шаг).
- Используйте подсказки , хотя большую часть времени я обращаюсь к подсказкам только тогда, когда ничего не помогает.В частности, я иногда использую подсказку LEADING , чтобы заставить оптимизатор запускаться с определенной таблицы (или пары таблиц).
- И наконец, вы будетевероятно, обнаружится, что более поздние выпуски имеют в целом более надежный оптимизатор.8i исполнилось 12 лет, и может быть настало время для обновления:)
Это действительно интересная тема.Оптимизатор оракула постоянно меняется (между выпусками), он улучшается с течением времени, даже если новые причуды иногда появляются по мере исправления дефектов.Если вы хотите узнать больше, я бы посоветовал Джонатану Льюису Oracle на основе стоимости: основы