Oracle 10g - оптимизируйте ГДЕ НЕ НУЛЛ

У нас есть Oracle 10g, и нам нужно запросить 1 таблицу (нет объединений) и отфильтровать строки, где 1 из столбцов имеет значение NULL. Когда мы это делаем - WHERE OurColumn IS NOT NULL - мы получаем полное сканирование таблицы на очень большой таблице - BAD BAD BAD. Столбец имеет индекс, но в этом случае он игнорируется. Есть ли какие-либо решения для этого?

Спасибо

Ответы

Ответ 1

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

Если есть только несколько строк NULL, оптимизатор прав.

Если вы абсолютно уверены, что доступ к индексу будет быстрее (т.е. у вас будет больше 75% строк с col1 IS NULL), тогда подскажите свой запрос:

SELECT  /*+ INDEX (t index_name_on_col1) */
        *
FROM    mytable t
WHERE   col1 IS NOT NULL

Почему 75%?

Поскольку использование INDEX SCAN для извлечения значений, не охватываемых индексом, подразумевает скрытое соединение на ROWID, которое стоит около 4 раз больше, чем сканирование таблицы.

Если диапазон индексов содержит более чем 25% строк, сканирование таблицы обычно выполняется быстрее.

Как упоминалось в Tony Andrews, фактор кластеризации является более точным методом измерения этого значения, но 25% по-прежнему является хорошим правилом.

Ответ 2

Оптимизатор будет принимать решение на основе относительной стоимости полного сканирования таблицы и использования индекса. Это в основном сводится к тому, сколько блоков нужно будет прочитать для удовлетворения запроса. 25%/75% правило большого пальца, упомянутое в другом ответе, упрощено: в некоторых случаях полное сканирование таблицы имеет смысл даже для того, чтобы получить 1% строк - т.е. Если эти строки будут распространяться по многим блокам.

Например, рассмотрим эту таблицу:

SQL> create table t1 as select object_id, object_name from all_objects;

Table created.
SQL> alter table t1 modify object_id null;

Table altered.

SQL> update t1 set object_id = null
  2  where mod(object_id,100) != 0
  3  /

84558 rows updated.

SQL> analyze table t1 compute statistics;

Table analyzed.

SQL> select count(*) from t1 where object_id is not null;

  COUNT(*)
----------
       861    

Как вы можете видеть, только около 1% строк в T1 имеют ненулевой объект_ид. Но из-за того, как я построил таблицу, эти 861 строки будут распределены более или менее равномерно по таблице. Поэтому запрос:

select * from t1 where object_id is not null;

вероятно, посетит почти каждый блок в T1, чтобы получить данные, даже если оптимизатор использовал индекс. Тогда имеет смысл отказаться от индекса и перейти на полное сканирование таблицы!

Ключевой статистикой, помогающей идентифицировать эту ситуацию, является фактор кластеризации индекса:

SQL> select clustering_factor from user_indexes where index_name='T1_IDX';

CLUSTERING_FACTOR
-----------------
              460

Это значение 460 довольно велико (по сравнению с 861 строками в индексе) и предполагает, что будет использоваться полное сканирование таблицы. См. эту статью DBAZine о факторах кластеризации.

Ответ 3

Если вы делаете select *, тогда имеет смысл делать сканирование таблицы, а не использовать индекс. Если вы знаете, какие столбцы вам интересны, вы можете создать охваченный индекс с этими коллами плюс тот, который вы используете условие IS NOT NULL.

Ответ 4

Это может зависеть от типа индекса, который у вас есть в таблице.

В большинстве индексов B-дерева не хранятся нулевые записи. Растровые индексы do сохраняют нулевые записи.

Итак, если у вас есть:

выберите * из таблицы где mycolumn имеет значение null

и у вас есть стандартный индекс B-дерева на mycolumn, тогда запрос не может использовать индекс, так как "null" не находится в индексе.

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

Ответ 5

Создайте индекс в этом столбце.

Чтобы убедиться, что этот индекс используется, он должен быть указан в индексе и в других столбцах.

ocdecio ответил:

Если вы делаете select *, тогда имеет смысл делать сканирование таблицы, а не использовать индекс.

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

Ответ 6

Также стоит проверить, актуальна ли статистика Oracle в таблице. Возможно, он не знает, что полное сканирование таблицы будет медленнее.

Ответ 7

База данных Oracle не индексирует нулевые значения вообще в обычных (b-tree) индексах, поэтому не может ее использовать, и вы не можете заставить Oracle использовать ее.

BR

Ответ 8

Использование подсказок должно выполняться только как работа, а не решение.

Как упоминалось в других ответах, нулевое значение недоступно в индексах B-TREE.

Поскольку вы знаете, что в этом столбце вы имеете в основном нулевые значения, могли бы вы, например, заменить нулевое значение на диапазон.

Это действительно зависит от вашего столбца и характера ваших данных, но обычно, если ваш столбец является типом даты, например:

where mydatecolumn is not null Может быть переведен в правило, говорящее: я хочу, чтобы все строки имели дату.

Тогда вы можете определенно сделать это: где mydatecolumn <= sysdate (in orlele)

Это приведет к возврату всех строк с датой и пропуском нулевых значений при использовании индекса в этом столбце без каких-либо намеков.

Ответ 9

См. http://www.oracloid.com/2006/05/using-index-for-is-null/

Если ваш индекс находится в одном отдельном поле, он НЕ будет использоваться. Попробуйте добавить фиктивное поле или константу в индекс:

create index tind on t(field_to_index, 1);