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
в качестве второго столбца это сделает индекс еще медленнее.
Если индекс действительно быстрее, чем, наверное, плохая статистика. Есть сотни способов, которыми статистика может быть плохой. Очень кратко, вот что я бы посмотрел сначала:
- запустите план объяснения с подсказкой и без подсказки индекса. Отключены ли мощности на 10x или более? Время от 10x или больше?
- если мощность выключена, убедитесь, что в таблице и индексе есть актуальная статистика, и вы используете разумный ESTIMATE_PERCENT (DBMS_STATS.AUTO_SAMPLE_SIZE почти всегда является лучшим для 11g).
- если время будет выключите, проверьте статистику рабочей нагрузки.
- вы используете параллельность? 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
возможно, это поможет вам.