[ПОСЛЕДНИЕ РЕДАКТИРОВАТЬ]
Мой ОРИГИНАЛЬНЫЙ ОТВЕТ относительно создания соответствующего индекса (имя, идентификатор) для замены индекса (имя) приведено ниже. (Это не был ответ на первоначальный вопрос, который запрещал любые изменения в базе данных.)
Вот утверждения, которые я не еще тестировал. Вероятно, есть какая-то очевидная причина, по которой они не будут работать. На самом деле я бы никогда не предложил предложить написания таких заявлений (рискуя получить полный удар по такому нелепому предложению).
Если эти запросы даже возвращают наборы результатов, набор результатов будет напоминать только набор результатов из запроса OP, почти случайно , используя причудливую гарантию о данных что дон предоставил нам. Этот оператор НЕ эквивалентен исходному SQL, эти операторы предназначены для особого случая , как описано Доном.
select m1.id
, m2.name
from (select min(t1.rowid) as min_rowid
, t1.id
from table1 t1
where t1.id is not null
group by t1.id
) m1
, (select min(t2.rowid) as min_rowid
, t2.name from table1 t2
where t2.name is not null
group by t2.name
) m2
where m1.min_rowid = m2.min_rowid
order
by m1.id
Давайте распакуем это:
- m1 - это встроенное представление, которое возвращает нам список различных значений идентификатора.
- м2 - это встроенное представление, которое дает нам список различных значений имени.
- материализуют виды м1 и м2
- соответствует ROWID из m1 и m2 , чтобы соответствовать
id
с name
Кто-то еще предложил идею слияния индексов. Ранее я отклонял эту идею, планируя оптимизатор, чтобы он соответствовал десяткам миллионов строк, не исключая ни одного из них.
При достаточно низкой мощности для идентификатора и имени и с правильным планом оптимизатора:
select m1.id
, ( select m2.name
from table1 m2
where m2.id = m1.id
and rownum = 1
) as name
from (select t1.id
from table1 t1
where t1.id is not null
group by t1.id
) m1
order
by m1.id
Давайте распакуем это
- m1 - это встроенное представление, которое возвращает нам список различных значений идентификатора.
- материализовать вид m1
- для каждой строки в m1 , запросите таблицу1, чтобы получить значение имени из одной строки (клавиша остановки)
ВАЖНОЕ ПРИМЕЧАНИЕ
Эти операторы ФУНДАМЕНТАЛЬНО отличаются от запроса OP. Они предназначены для возврата РАЗНОГО результирующего набора, чем запрос OP. случается , чтобы вернуть желаемый набор результатов из-за странной гарантии данных. Дон сказал нам, что name
определяется id
. (Верно ли обратное? Определяется ли id
как name
? Есть ли у нас ЗАЯВЛЕННАЯ ГАРАНТИЯ, не обязательно обеспечиваемая базой данных, но гарантирующая, что мы можем воспользоваться?) Для любого значения ID
для каждой строки с этим значением ID
будет то же значение NAME
. (И мы также гарантируем, что обратное утверждение верно, что для любого значения NAME
каждая строка с этим значением NAME
будет иметь одинаковое значение ID
?)
Если это так, возможно, мы сможем использовать эту информацию. Если ID
и NAME
появляются в разных парах, нам нужно найти только одну конкретную строку. «Пара» будет иметь соответствующий ROWID, который, как правило, доступен для каждого из существующих индексов. Что если мы получим минимальный ROWID для каждого ID
, и получим минимальный ROWID для каждого NAME
. Разве мы не можем тогда сопоставить ID
с NAME
на основе ROWID, который содержит пару? Я думаю, что это может сработать, учитывая достаточно низкий уровень мощности. (То есть, если мы имеем дело только с сотнями ROWID, а не с десятками миллионов.)
[/ ПОСЛЕДНИЕ РЕДАКТИРОВАТЬ]
[EDIT]
Вопрос теперь дополнен информацией, касающейся таблицы, которая показывает, что столбцы ID
и столбец NAME
допускают значения NULL. Если Дон может жить без значений NULL, возвращаемых в наборе результатов, то добавление предиката IS NOT NULL в оба этих столбца может позволить использовать индекс. (ПРИМЕЧАНИЕ: в индексе Oracle (B-Tree) значения NULL НЕ отображаются в индексе.)
[/ EDIT]
ОРИГИНАЛЬНЫЙ ОТВЕТ:
создать соответствующий индекс
create index table1_ix3 on table_1 (name,id) ... ;
Хорошо, это , а не ответ на вопрос, который вы задали , но это правильный ответ для исправления проблемы с производительностью. (Вы указали отсутствие изменений в базе данных, но в этом случае изменение базы данных является правильным ответом.)
Обратите внимание, что если у вас есть индекс, определенный для (name,id)
, то вам (весьма вероятно) не нужен индекс для (name)
, так как оптимизатор будет рассматривать ведущий столбец name
в другом индексе.
(ОБНОВЛЕНИЕ: как кто-то более проницательный, чем я указывал, я даже не рассматривал возможность того, что существующие индексы являются растровыми индексами, а не индексами B-дерева ...)
Пересмотрите свою потребность в наборе результатов ... Вам нужно вернуть id
, или будет достаточно возврата name
.
select distinct name from table1 order by name;
Для определенного имени вы можете отправить второй запрос, чтобы получить связанный id
, если и когда вам это нужно ...
select id from table1 where name = :b1 and rownum = 1;
Если вы действительно нуждаетесь в указанном наборе результатов, вы можете попробовать некоторые альтернативные варианты, чтобы посмотреть, улучшится ли производительность. Я не очень надеюсь на что-либо из этого:
select /*+ FIRST_ROWS */ DISTINCT id, name from table1 order by id;
или
select /*+ FIRST_ROWS */ id, name from table1 group by id, name order by name;
или
select /*+ INDEX(table1) */ id, min(name) from table1 group by id order by id;
ОБНОВЛЕНИЕ: как проницательно отмечали другие, с помощью этого подхода мы тестируем и сравниваем производительность альтернативных запросов, что является своего рода подходом типа «попал в ловушку». (Я не согласен, что это случайно, но я согласен, что это удар или мисс.)
ОБНОВЛЕНИЕ: Том предлагает подсказку ALL_ROWS. Я не учел это, потому что был действительно сосредоточен на получении плана запроса с использованием INDEX. Я подозреваю, что запрос OP выполняет полное сканирование таблицы, и, вероятно, время занимает не сканирование, а уникальная операция сортировки (<10g) или операция хеширования (10gR2 +), которая занимает время. (Отсутствует временная статистика и трассировка события 10046, я просто догадываюсь здесь.) Но опять же, может быть, это сканирование, которое знает, что высокая отметка на столе может быть выходом из огромного пространства пустых блоков. </p>
Практически само собой разумеется, что статистика в таблице должна быть актуальной, и мы должны использовать SQL * Plus AUTOTRACE или, по крайней мере, EXPLAIN PLAN для просмотра планов запросов.
Но ни один из предложенных альтернативных запросов действительно не решает проблему производительности.
Возможно, что подсказки повлияют на оптимизатор, который выберет другой план, в основном удовлетворяя ORDER BY из индекса, но я не очень надеюсь на это. (Я не думаю, что подсказка FIRST_ROWS работает с GROUP BY, подсказка INDEX может.) Я вижу потенциал для такого подхода в сценарии, где есть блоки блоков данных, которые являются пустыми и редко заполненными, и ny получает доступ к данным. блоков через индекс, фактически может быть значительно меньше блоков данных, извлеченных в память ... но этот сценарий будет скорее исключением, чем нормой.
ОБНОВЛЕНИЕ: Как указывает Роб ван Вийк, использование средства трассировки Oracle является наиболее эффективным подходом к выявлению и решению проблем с производительностью.
Без вывода EXPLAIN PLAN или SQL * Plus AUTOTRACE, я просто догадываюсь здесь.
Я подозреваю, что проблема с производительностью, которая у вас есть сейчас, заключается в том, что на блоки данных таблицы необходимо ссылаться, чтобы получить указанный набор результатов.
Не обойтись, запрос не может быть удовлетворен только из индекса, так как нет индекса, содержащего столбцы NAME
и ID
, с ID
или NAME
столбец как ведущий столбец. Два других «быстрых» OP-запроса могут быть выполнены из индекса без необходимости ссылаться на строку (блоки данных).
Даже если план оптимизатора для запроса должен был использовать один из индексов, он все равно должен извлечь соответствующую строку из блока данных, чтобы получить значение для другого столбца. А без предиката (без предложения WHERE) оптимизатор, скорее всего, выберет полное сканирование таблицы и, вероятно, выполнит операцию сортировки (<10g). (Опять же, EXPLAIN PLAN покажет план оптимизатора, как и AUTOTRACE.) </p>
Я также предполагаю (большое предположение), что оба столбца определены как NOT NULL.
Вы также можете рассмотреть определение таблицы как таблицы с индексами (IOT), особенно если это только два столбца в таблице. (IOT - не панацея, у него свой собственный набор проблем с производительностью.)
Вы можете попробовать переписать запрос (если только это не изменение базы данных, которое также является вербальным). В наших средах баз данных мы считаем запрос такой же частью базы данных, как таблицы и индексы.)
Опять же, без предиката оптимизатор, скорее всего, не будет использовать индекс. Существует вероятность того, что план запроса может использовать один из существующих индексов для быстрого возврата первых строк, добавив подсказку, протестировав комбинацию из:
select /*+ INDEX(table1) */ ...
select /*+ FIRST_ROWS */ ...
select /*+ ALL_ROWS */ ...
distinct id, name from table1;
distinct id, name from table1 order by id;
distinct id, name from table1 order by name;
id, name from table1 group by id, name order by id;
id, min(name) from table1 group by id order by id;
min(id), name from table1 group by name order by name;
С помощью подсказки вы можете повлиять на оптимизатор, чтобы он использовал индекс, и это может избежать операции сортировки, но в целом потребуется больше времени, чтобы вернуть весь набор результатов.
(ОБНОВЛЕНИЕ: кто-то еще указал, что оптимизатор может выбрать объединение двух индексов на основе ROWID. Это возможно, но без предиката для удаления некоторых строк, это, вероятно, будет гораздо более дорогим подходом (при сопоставлении 10 с миллионов ROWID) из двух индексов, особенно когда ни одна из строк не будет исключена на основе совпадения.)
Но все эти теоретизирования не сводятся к приседу без некоторой статистики производительности.
В отсутствие изменения чего-либо еще в базе данных, единственная надежда (я могу думать) о том, что вы ускорите запрос, состоит в том, чтобы убедиться, что операция сортировки настроена так, чтобы (требуемая) операция сортировки могла выполняться в памяти, а не на диске. Но это не совсем правильный ответ. Оптимизатор может вообще не выполнять операцию сортировки, вместо этого он может выполнять операцию хеширования (10gR2 +), и в этом случае это должно быть настроено. Операция сортировки с моей стороны - всего лишь предположение, основанное на прошлом опыте работы с Oracle 7.3, 8, 8i, 9i.)
Серьезный администратор БД будет иметь больше проблем с тем, что вы будете использовать параметры SORT_AREA_SIZE
и / или HASH_AREA_SIZE
для ваших сеансов, чем он будет создавать правильные индексы. (И эти параметры сеанса являются «старой школой» для версий до 10g автоматического управления памятью.)
Покажите вашему администратору базы данных спецификацию для набора результатов, позвольте администратору настроить его.