Stumped - Oracle не будет использовать индекс, когда значение указано, но будет, когда функция возвращает то же значение - PullRequest
1 голос
/ 06 марта 2012

В настоящее время я работаю с базой данных, которая имеет два индекса для конкретной таблицы. Индекс, который я хочу, имеет два столбца: «Имя» (varchar2) и «Время» (число). Когда я пишу запрос

SELECT SOMETHING
  FROM MYTABLE
 WHERE NAME = 'SOME-NAME'
   AND TIME BETWEEN STARTVALUE AND ENDVALUE

(где STARTVALUE и ENDVALUE - числа) он не использует индекс. Однако, если я использую следующий запрос вместо

SELECT SOMETHING
  FROM MYTABLE
 WHERE NAME = 'SOME-NAME'
   AND TIME BETWEEN MY_FUNC('STARTQUAL') AND MY_FUNC('ENDQUAL')

это так.

Единственное различие, которое я могу вспомнить, заключается в том, что MY_FUNC явно возвращает значение типа NUMBER - возможно ли, что оптимизатор запросов запутается в типе данных для STARTVALUE и ENDVALUE, указанных явно, и отказывается использовать индекс (я видел причиной были некоторые похожие потоки, в которых упоминался конфликт типов).

Примечание:

  1. Значение, возвращаемое MY_FUNC, равно ИМЕННО тому же значению, которое я указываю в первом запросе.

  2. Индекс, о котором идет речь, НЕОБХОДИМО (абсолютно без сомнения), правильный индекс, который нужно использовать, и время выполнения на несколько порядков быстрее, если оно есть.

  3. Я даже указал подсказку в первом запросе, и он отказывается использовать индекс.

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

Заранее благодарим за помощь.

Ответы [ 2 ]

2 голосов
/ 06 марта 2012

Кроме того, Oracle может по-разному оптимизировать запросы в зависимости от того, включает ли запрос буквальные или связанные значения.

SELECT SOMETHING
  FROM MYTABLE
 WHERE NAME = 'SOME-NAME'
   AND TIME BETWEEN 7 AND 41;

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

Однако такой запрос:

SELECT SOMETHING
  FROM MYTABLE
 WHERE NAME = 'SOME-NAME'
   AND TIME BETWEEN MY_FUNC('7') AND MY_FUNC('41');

, вероятно, будет оптимизирован как семантически эквивалентный:

SELECT SOMETHING
  FROM MYTABLE
 WHERE NAME = 'SOME-NAME'
   AND TIME BETWEEN :some_bind AND :some_other_bind;

Поскольку Oracle не знает, что делает MY_FUNC('7') - или даже MY_FUNC('7') всегда будет возвращать одно и то же значение 7 - если вы не сообщили Oracle о детерминированности функции. Таким образом, мой опыт показывает, что Oracle в большинстве случаев принимает удар в темноте и предпочитает индекс с высоким коэффициентом кластеризации. Кажется, кажется, что даже если индекс не лучший выбор, по крайней мере, он сводит к минимуму риск падения, посещая как можно меньше блоков данных.

Я рекомендую выяснить для себя, почему он ведет себя по-разному - выполните трассировку 10053 каждого запроса:

alter session set events = '10053 trace name context forever;
run sql
alter session set events = '10053 trace name context off;
0 голосов
/ 06 марта 2012
SELECT SOMETHING
FROM MYTABLE
WHERE NAME = 'SOME-NAME'
AND TIME BETWEEN STARTVALUE AND ENDVALUE

Здесь у вас есть TIME, который является НОМЕРОМ, и STARTVALUE и ENDVALUE, которые являются строками (согласно вашему комментарию).Поэтому выполняется неявное преобразование, т. Е. Ваш запрос выполняется эффективно:

SELECT SOMETHING
FROM MYTABLE
WHERE NAME = 'SOME-NAME'
AND TO_CHAR(TIME) BETWEEN STARTVALUE AND ENDVALUE

Если у вас нет индекса на основе функций для TO_CHAR(TIME), он не будет использовать индекс.

Следовательно, вы должны сообщить Oracle, что вы всегда ожидаете, что строковые параметры будут конвертируемыми в числа, например:

SELECT SOMETHING
FROM MYTABLE
WHERE NAME = 'SOME-NAME'
AND TIME BETWEEN TO_NUMBER(STARTVALUE) AND TO_NUMBER(ENDVALUE)

(В любом случае всегда рекомендуется избегать неявных преобразований, особенно в запросах)

...