Oracle 10g - оптимизировать, ГДЕ НЕ НУЛЬ - PullRequest
15 голосов
/ 06 апреля 2009

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

Спасибо

Ответы [ 9 ]

23 голосов
/ 06 апреля 2009

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

Если есть только несколько 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% все еще является хорошим эмпирическим правилом.

16 голосов
/ 06 апреля 2009

Оптимизатор примет решение на основе относительной стоимости полного сканирования таблицы и использования индекса. Это в основном сводится к тому, сколько блоков нужно будет прочитать, чтобы удовлетворить запрос. Практическое правило 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 строкой в ​​индексе) и предполагает, что будет использовано полное сканирование таблицы. См. эту статью DBAZine о факторах кластеризации .

2 голосов
/ 06 апреля 2009

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

1 голос
/ 22 апреля 2009

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

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

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

выберите * из mytable где mycolumn равен нулю

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

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

0 голосов
/ 02 мая 2016

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

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

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

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

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

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

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

0 голосов
/ 30 января 2015

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

BR

0 голосов
/ 24 февраля 2010

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

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

create index tind on t(field_to_index, 1); 
0 голосов
/ 23 апреля 2009

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

0 голосов
/ 06 апреля 2009

Создать индекс для этого столбца.

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

ocdecio ответил:

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...