Oracle 11g: индекс, не используемый в"select distinct" -query

мой вопрос касается Oracle 11g и использования индексов в SQL-запросах.

в моей базе данных есть таблица, которая структурирована следующим образом:

Table tab (
  rowid NUMBER(11),
  unique_id_string VARCHAR2(2000),
  year NUMBER(4),
  dynamic_col_1 NUMBER(11),
  dynamic_col_1_text NVARCHAR2(2000)
 ) TABLESPACE tabspace_data;

Я создал два индекса:

CREATE INDEX Index_dyn_col1 ON tab (dynamic_col_1, dynamic_col_1_text) TABLESPACE tabspace_index;
CREATE INDEX Index_unique_id_year ON tab (unique_id_string, year) TABLESPACE tabspace_index;

таблица содержит от 1 до 2 миллионов записей. Я извлекаю данные из него, выполнив следующую команду SQL:

SELECT distinct
 "sub_select"."dynamic_col_1" "AS_dynamic_col_1","sub_select"."dynamic_col_1_text" "AS_dynamic_col_1_text"
FROM 
(
    SELECT "tab".*  FROM "tab"
    where "tab".year = 2011
) "sub_select"

к сожалению, запрос требует около 1 часа для выполнения, хотя я создал оба описанных индекса выше. План explain показывает, что Oracle использует "полный доступ к таблице", т. е. полное сканирование таблицы. Почему индекс не используется?

в качестве эксперимента я протестировал следующую команду SQL:

SELECT DISTINCT
 "dynamic_col_1" "AS_dynamic_col_1", "dynamic_col_1_text" "AS_dynamic_col_1_text"
 FROM "tab"

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

в моей реальной базе данных таблица содержит больше индексированных столбцов, таких как" dynamic_col_1 "и"dynamic_col_1_text". Весь индексный файл имеет размер около 50 ГБ.

еще несколько информация:

  • база данных Oracle 11g установлена на моем локальном компьютере.
  • я использую Windows 7 Enterprise 64bit.
  • весь индекс разделен на 3 dbf-файла размером около 50 ГБ.

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

спасибо заранее.

[01.10.2011: обновление]

Я думаю, что нашел решение проблемы. Обе колонки dynamic_col_1 и dynamic_col_1_text допускают. После изменения таблицы, чтобы запретить "NULL" - значения в обоих столбцах и добавить новый индекс исключительно для столбца year, Oracle выполняет быстрое сканирование индекса. Преимущество заключается в том, что выполнение запроса занимает около 5 секунд, а не 1 час, как раньше.

6 ответов


вы уверены, что доступ к индексу будет быстрее, чем полное сканирование таблицы? По очень приблизительной оценке, полное сканирование таблицы в 20 раз быстрее, чем чтение индекса. Если tab имеет более 5% данных в 2011 году неудивительно, что Oracle будет использовать полное сканирование таблицы. И, как упоминали @Dan и @Ollie, с year в качестве второго столбца это сделает индекс еще медленнее.

Если индекс действительно быстрее, чем, наверное, плохая статистика. Есть сотни способов, которыми статистика может быть плохой. Очень кратко, вот что я бы посмотрел сначала:

  1. запустите план объяснения с подсказкой и без подсказки индекса. Отключены ли мощности на 10x или более? Время от 10x или больше?
  2. если мощность выключена, убедитесь, что в таблице и индексе есть актуальная статистика, и вы используете разумный ESTIMATE_PERCENT (DBMS_STATS.AUTO_SAMPLE_SIZE почти всегда является лучшим для 11g).
  3. если время будет выключите, проверьте статистику рабочей нагрузки.
  4. вы используете параллельность? Oracle всегда предполагает почти линейное улучшение параллелизма, но на рабочем столе с одним жестким диском вы, вероятно, не увидите никакого улучшения вообще.

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


ваш индекс должен быть:

CREATE INDEX Index_year 
ON tab (year) 
TABLESPACE tabspace_index;

кроме того, ваш запрос может быть:

SELECT DISTINCT
       dynamic_col_1 "AS_dynamic_col_1",
       dynamic_col_1_text "AS_dynamic_col_1_text"
  FROM tab
 WHERE year = 2011;

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

надеюсь, что это помогает...


У меня нет экземпляра Oracle, поэтому это несколько догадок, но я склонен сказать, что это потому, что у вас есть составной индекс в неправильном порядке. Если бы ты ... --0--> в качестве первого столбца индекса, он может использовать его.


свой второй тестовый запрос:

SELECT DISTINCT
"dynamic_col_1" "AS_dynamic_col_1", "dynamic_col_1_text" "AS_dynamic_col_1_text"
FROM "tab"

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

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


Я не знаю, имеет ли это значение, но я проверил следующий запрос:

SELECT DISTINCT
"dynamic_col_1" "AS_dynamic_col_1", "dynamic_col_1_text" "AS_dynamic_col_1_text"
FROM "tab"
WHERE "dynamic_col_1" = 123 AND "dynamic_col_1_text" = 'abc'

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

колонки dynamic_col_1 и dynamic_col_1_text допускают. Влияет ли это на использование индекса?

01.10.2011: обновление]

Я думаю, что нашел решение проблемы. Оба столбца dynamic_col_1 и dynamic_col_1_text являются nullable. После изменения таблицы чтобы запретить "NULL"-значения в обоих столбцах и добавить новый индекс только для года столбца, Oracle выполняет быстрое сканирование индекса. Преимущество заключается в том, что выполнение запроса занимает около 5 секунд, а не 1 час, как раньше.


попробуйте это:

1) Создайте индекс в поле год (см. ответ Олли).

2), а затем использовать этот запрос:

SELECT DISTINCT
dynamic_col_1
,dynamic_col_1_text
FROM tab 
WHERE ID (SELECT ID FROM tab WHERE year=2011)

или

SELECT DISTINCT
dynamic_col_1
,dynamic_col_1_text
FROM tab 
WHERE ID (SELECT ID FROM tab WHERE year=2011)
GROUP BY dynamic_col_1, dynamic_col_1_text

возможно, это поможет вам.