Почему этот запрос выполняет полное сканирование таблицы? - PullRequest
4 голосов
/ 27 февраля 2009

Запрос:

SELECT tbl1.*
   FROM tbl1 
JOIN tbl2
     ON (tbl1.t1_pk  = tbl2.t2_fk_t1_pk
AND tbl2.t2_strt_dt <= sysdate
AND tbl2.t2_end_dt  >= sysdate)
JOIN tbl3 on (tbl3.t3_pk = tbl2.t2_fk_t3_pk
AND tbl3.t3_lkup_1 = 2577304
AND tbl3.t3_lkup_2 = 1220833)
where tbl2.t2_lkup_1   = 1020000002981587;

Факты:

  • Oracle XE
  • tbl1.t1_pk является первичным ключом.
  • tbl2.t2_fk_t1_pk - это внешний ключ в этом столбце t1_pk.
  • tbl2.t2_lkup_1 проиндексирован.
  • tbl3.t3_pk - это первичный ключ.
  • tbl2.t2_fk_t3_pk - это внешний ключ в этом столбце t3_pk.

Объяснить план для базы данных с 11 000 строк в таблице 1 и 3500 строк в Таблица 2 показывает, что она выполняет полное сканирование таблицы на таблице TB1. Мне кажется что это должно быть быстрее, если бы он мог выполнить запрос индекса на tbl1.

Объяснить план для базы данных с 11 000 строк в таблице 1 и 3500 строк в Таблица 2 показывает, что она выполняет полное сканирование таблицы на таблице TB1. Мне кажется что это должно быть быстрее, если бы он мог выполнить запрос индекса на tbl1.

Обновление: я попробовал подсказку, которую предложили некоторые из вас, и стоимость объяснения стала намного хуже! Теперь я в замешательстве.

Дальнейшее обновление: я наконец-то получил доступ к копии рабочей базы данных, и «объяснить план» показал это с использованием индексов и с гораздо более низкой стоимостью запрос. Я предполагаю, что больше данных (более 100 000 строк в Tbl1 и 50000 строк в Табл.2) было то, что потребовалось, чтобы решить, что индексы того стоили. Спасибо всем, кто помог. Я до сих пор считаю настройку производительности Oracle черным искусством, но я рад, что некоторые из вас это понимают.

Дальнейшее обновление: я обновил вопрос по просьбе моего бывшего работодателя. Им не нравится, когда их имена таблиц отображаются в запросах Google. Я должен был знать лучше.

Ответы [ 8 ]

5 голосов
/ 27 февраля 2009

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

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

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

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

3 голосов
/ 27 февраля 2009

Было бы полезно увидеть оценки количества строк оптимизатора, которых нет в опубликованном вами выводе разработчика SQL.

Я отмечаю, что два поиска индекса, которые он выполняет, - ДИАПАЗОН СКАНИРОВАНИЯ, а не УНИКАЛЬНЫЙ СКАН. Таким образом, его оценки того, сколько строк возвращается, легко могут быть далеки (независимо от того, актуальна статистика или нет).

Я предполагаю, что его оценка итогового числа строк по TABLE ACCESS TBL2 довольно высока, поэтому он считает, что он найдет большое количество совпадений в TBL1, и поэтому решает скорее выполнить полное сканирование / соединение хешей чем сканирование вложенного цикла / индекса.

Для развлечения вы можете запустить запрос с включенным событием 10053 и получить трассировку, показывающую вычисления, выполненные оптимизатором.

2 голосов
/ 27 февраля 2009

Oracle пытается вернуть набор результатов с наименьшим количеством требуемого ввода-вывода (обычно это имеет смысл, потому что ввод-вывод медленный). Индексы принимают не менее 2 вызовов ввода / вывода. один для индекса и один для таблицы. Обычно больше, в зависимости от размера индекса и размеров таблиц и количества возвращаемых записей, где они находятся в файле данных, ...

Вот где приходит статистика. Предположим, ваш запрос вернет 10 записей. Оптимизатор может рассчитать, что при использовании индекса потребуется 10 вызовов ввода / вывода. Допустим, ваша таблица, согласно статистике, находится в 6 блоках в файле данных. Oracle быстрее выполнит полное сканирование (6 операций ввода-вывода), затем прочитает индекс, прочитает таблицу, прочитает, затем индекс для следующего соответствующего ключа, прочитает таблицу и т. Д.

Так что в вашем случае таблица может быть очень маленькой. Статистика может быть отключена.

Я использую следующее, чтобы собрать статистику и настроить ее для своих точных нужд:

begin

 DBMS_STATS.GATHER_TABLE_STATS(ownname
=> '&owner' ,tabname => '&table_name', estimate_percent => dbms_stats.AUTO_SAMPLE_SIZE,granularity
=> 'ALL', cascade  => TRUE); 

 -- DBMS_STATS.GATHER_TABLE_STATS(ownname
=> '&owner' ,tabname => '&table_name',partname => '&partion_name',granularity => 'PARTITION', estimate_percent => dbms_stats.AUTO_SAMPLE_SIZE, cascade 
=> TRUE);

 -- DBMS_STATS.GATHER_TABLE_STATS(ownname
=> '&owner' ,tabname => '&table_name',partname => '&partion_name',granularity => 'PARTITION', estimate_percent => dbms_stats.AUTO_SAMPLE_SIZE, cascade 
=> TRUE,method_opt  => 'for all indexed columns size 254');

end;
1 голос
/ 27 февраля 2009

Попробуйте добавить индексную подсказку.

SELECT /*+ index(tbl1 tbl1_index_name) */ .....

Иногда Oracle просто не знает, какой индекс использовать.

1 голос
/ 27 февраля 2009

Вы можете сказать только, посмотрев план запроса, который создает оптимизатор / исполнитель SQL. Он будет по меньшей мере частичным на основе статистики индекса, которая не может быть предсказана только из определения (и, следовательно, может изменяться со временем).

SQL Management Studio для SQL Server 2005/2008, Query Analyzer для более ранних версий.

(Не могу вспомнить правильные названия инструментов для Oracle.)

0 голосов
/ 21 мая 2015

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

SHOW PARAMETER optimizer_index_cost_adj;
[...]
ALTER SESSION SET optimizer_index_cost_adj = 10;

SHOW PARAMETER OPTIMIZER_MODE;
[...]
ALTER SESSION SET OPTIMIZER_MODE=FIRST_ROWS_100;

и не забудьте проверить реальное время выполнения, иногда план не является реальным миром;)

0 голосов
/ 27 февраля 2009

Похоже, что индекс для таблицы tbl1 не берется. Удостовериться у вас есть индекс для столбца t2_lkup_1, и он не должен быть многостолбцовым, в противном случае индекс не применяется.

(в дополнение к тому, что комментирует Мэтт) По вашему запросу вы присоединяетесь, потому что хотите отфильтровать записи, которые не следует делать JOIN, что может увеличить количество элементов для набора результатов из таблица tbl1, если есть повторяющиеся совпадения из. См. комментарий Джеффа Этвуда

Попробуйте это, который использует функцию существующие и присоединиться (что действительно быстро на оракуле)

select *
  from tbl1 
 where tbl2.t2_lkup_1 = 1020000002981587 and
       exists (
         select *
           from tbl2, tbl3 
          where tbl2.t2_fk_t1_pk = tbl1.t1_pk and
                tbl2.t2_fk_t3_pk = tbl3.t3_pk  and
                sysdate between tbl2.t2_strt_dt and tbl2.t2_end_dt and
                tbl3.t3_lkup_1 = 2577304 and
                tbl3.t3_lkup_2 = 1220833);

0 голосов
/ 27 февраля 2009

Видимо, этот запрос дает тот же план:

SELECT tbl1.*   
FROM tbl1 
JOIN tbl2 ON (tbl1.t1_pk  = tbl2.t2_fk_t1_pk)
JOIN tbl3 on (tbl3.t3_pk = tbl2.t2_fk_t3_pk)
where tbl2.t2_lkup_1   = 1020000002981587
AND tbl2.t2_strt_dt <= sysdate
AND tbl2.t2_end_dt  >= sysdate
AND tbl3.t3_lkup_1 = 2577304
AND tbl3.t3_lkup_2 = 1220833;

Что произойдет, если переписать этот запрос на:

SELECT tbl1.*    
FROM  tbl1 
,     tbl2
,     tbl3  
where tbl2.t2_lkup_1   = 1020000002981587 
AND   tbl1.t1_pk  = tbl2.t2_fk_t1_pk 
AND   tbl3.t3_pk = tbl2.t2_fk_t3_pk 
AND   tbl2.t2_strt_dt <= sysdate 
AND   tbl2.t2_end_dt  >= sysdate 
AND   tbl3.t3_lkup_1 = 2577304 
AND   tbl3.t3_lkup_2 = 1220833;
...