2009-02-26 2 views
4

Запрос:Почему этот запрос выполняет полное сканирование таблицы?

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.

Объяснить план на базе данных с 11000 строк в tbl1 и 3500 строк в tbl2 показывает, что он делает полное сканирование таблицы на tbl1. Мне кажется, что он должен быть быстрее, если он может сделать индексный запрос на tbl1.

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

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

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

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

+0

Какой-то садист выбирает имена таблиц и столбцов? Или это результат обфускатора кода? –

+0

Это не совсем «полный» план объяснений. AutoTrace в SQLPlus дает намного больше информации. –

+0

Вот некоторые из худших имен таблиц, которые я видел через некоторое время. – Kibbee

ответ

3

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

Отметьте, что два индекса, которые он выполняет, являются RANGE SCAN, а не UNIQUE SCAN. Таким образом, его оценки количества возвращаемых строк могут быть легко далеки (независимо от того, являются ли статистики актуальными или нет).

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

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

+0

Дэйв должен быть чертовски хорошим тюнером, он написал все, что я собирался и многое другое. ;-) Серьезно нам нужен столбец мощности. Используйте DBMS_XPLAN, чтобы получить что-то полезное. – 2009-02-26 22:45:23

1

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

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

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

5

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

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

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

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

1

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

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

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

+0

Это код, он не сдаётся, потому что <истеричный женский голос> Я просто не знаю, какой из них выбрать! он предпочитает не использовать индекс, потому что стоимость этого была ВЫШЕ, чем стоимость выполнения чего-то еще. – 2009-02-26 22:34:54

+0

Итак ... предоставление шоколадных конфет Oracle не было хорошей идеей? – Barry

0

По-видимому, этот запрос дает тот же план:

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; 
+0

На самом деле вывод Explain Plan выглядит точно так же. –

+0

Ваш второй пример - это то, как выглядел запрос, когда я начал. Я изменил его, чтобы объединиться, но он не изменил план объяснения. –

+0

Oracle все равно превращает их в один и тот же промежуточный внутренний формат. Поэтому, если не будет ошибки при разборе, он будет иметь тот же план. –

2

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

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

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

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

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; 
0

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

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

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

 
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

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

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; 

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

Смежные вопросы