2009-04-06 3 views
15

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

Благодаря

ответ

20

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

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

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

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

Почему 75%?

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

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

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

+1

Quassnoi, где вы получаете 75%? Если имеется миллион строк, и только один из них равен нулю, почему использование индекса в этих столбцах будет медленнее, чем сканирование таблицы? – tpdi

+1

Поскольку индекс ссылается на скрытое соединение на ROWID, которое стоит примерно в 4 раза больше, чем сканирование таблицы. Является ли селективность индекса менее 25%, сканирование таблицы обычно происходит быстрее. – Quassnoi

+2

В полном сканировании таблицы вы просто перебираете все строки таблицы; если вы выполняете сканирование индекса, вам сначала нужно прочитать индекс, а затем прочитать таблицу. С определенной точки стоимость чтения индекса выше, чем просто чтение всей таблицы. – andri

2

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

0

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

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

ocdecio ответил:

Если вы делаете выбор *, то это имело бы смысл делать сканирование таблицы, а не с помощью индекса.

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

+0

Отличные комментарии по выбору * Просто для уточнения - мы никогда не пользователь SELECT * по другим причинам - мы всегда включаем наш список столбцов в предложениях SELECT. –

15

Оптимизатор будет принимать решение на основе относительной стоимости полного сканирования таблицы и использования индекса. Это в основном сводится к тому, сколько блоков нужно будет прочитать для удовлетворения запроса. 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 имеют ненулевой object_id.Но из-за того, как я построил таблицу, эти 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 строк в индексе), и предполагает, что полное сканирование таблицы будет использоваться. См. this DBAZine article on clustering factors.

1

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

Большинство индексов B-дерева не магазины нулевые записи. Индексы растровых изображений do хранить нулевые записи.

Так что, если у вас есть:

выбери * из туЬаЫх где MyColumn является нулевым

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

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

+1

Вопрос, связанный с поиском 'не null ', а не' null'. – KajMagnus

+0

Тем не менее, это полезная информация –

0

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

-1

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

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

create index tind on t(field_to_index, 1); 
0

Oracle делать нулевые значения не индекс в регулярных (б-дерево) индексах, поэтому он не может использовать его, ни вы можете» t заставляйте базу данных oracle использовать ее.

BR

+0

Этот вопрос о 'НЕ является нулевым'. Все соответствующие значения будут в индексе. –

0

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

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

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

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

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

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^