Насколько эффективно Oracle обрабатывает очень длинный список операторов IN - PullRequest
6 голосов
/ 13 марта 2012

У меня есть следующий запрос (это упрощенная версия гораздо более сложного запроса):

SELECT * FROM TPM_TASK
WHERE (PROJECTID, VERSIONID) IN ((3,1), (24,1), (4,1))

В коде я создам этот список ключей (PROJECTID,VERSIONID) программно, и этот список потенциально можетбыть длиной в пару тысяч пар.

Мой вопрос заключается в том, как Oracle оптимизирует этот запрос, если индексировать ProjectId и VersionId.Будет ли список преобразован в хеш-таблицу, аналогичную join для временной таблицы?Или каждый поиск ключа будет выполняться по одному?

Я попытался выполнить этот запрос в своей тестовой базе данных и получил:

SELECT STATEMENT    68.0    68  2989732 19  8759    68                  ALL_ROWS                                            
   TABLE ACCESS (FULL)  68.0    68  2989732 19  8759    1   TPMDBO  TPM_TASK    FULL    TABLE   ANALYZED    1

Однако я считаю, что в этой базе данных недостаточно данныхчтобы гарантировать сканирование индекса.Я попробовал запрос на производство и получил:

SELECT STATEMENT    19.0    19  230367  23  9683    19                  ALL_ROWS                                            
   INLIST ITERATOR                      1                                                               
      TABLE ACCESS (BY INDEX ROWID) 19.0    19  230367  23  9683    1   TPMDBO  TPM_TASK    BY INDEX ROWID  TABLE   ANALYZED    1                                       
         INDEX (RANGE SCAN) 4.0 4   64457   29      1   TPMDBO  TPM_H1_TASK RANGE SCAN  INDEX   ANALYZED                1                           

Это похоже на индекс, однако я не уверен, что означает INLIST ITERATOR .Я предполагаю, что это означает, что Oracle выполняет итерацию по списку и выполняет доступ к таблице для каждого элемента в списке, что, вероятно, было бы не слишком эффективно с тысячами ключей.Однако, возможно, Oracle достаточно умен, чтобы оптимизировать это лучше, если бы я на самом деле сделал дал ему несколько тысяч ключей.

ПРИМЕЧАНИЕ: Я не хочу загружать эти ключив временную таблицу, потому что, честно говоря, мне не нравится, как временные таблицы работают под Oracle, и они обычно приводят к большему разочарованию, чем они стоят (по моему мнению неэкспертов в любом случае.)

Ответы [ 2 ]

8 голосов
/ 14 марта 2012

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

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

К комментарию Бранко, в то время как Oracle ограничен 1000 литералами в списке IN, то есть, только если вы используете «нормальный» синтаксис, то есть

WHERE projectID IN (1,2,3,...,N)

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

Так, например, я получу ошибку, если создам запрос с 2000 элементами в INlist

SQL> ed
Wrote file afiedt.buf

  1  declare
  2    l_sql_stmt varchar2(32000);
  3    l_cnt      integer;
  4  begin
  5    l_sql_stmt := 'select count(*) from emp where empno in (';
  6    for i in 1..2000
  7    loop
  8      l_sql_stmt := l_sql_stmt || '(1),';
  9    end loop;
 10    l_sql_stmt := rtrim(l_sql_stmt,',') || ')';
 11  --  p.l( l_sql_stmt );
 12    execute immediate l_sql_stmt into l_cnt;
 13* end;
SQL> /
declare
*
ERROR at line 1:
ORA-01795: maximum number of expressions in a list is 1000
ORA-06512: at line 12

Но только если я использую синтаксис кортежа

SQL> ed
Wrote file afiedt.buf

  1  declare
  2    l_sql_stmt varchar2(32000);
  3    l_cnt      integer;
  4  begin
  5    l_sql_stmt := 'select count(*) from emp where (empno,empno) in (';
  6    for i in 1..2000
  7    loop
  8      l_sql_stmt := l_sql_stmt || '(1,1),';
  9    end loop;
 10    l_sql_stmt := rtrim(l_sql_stmt,',') || ')';
 11  --  p.l( l_sql_stmt );
 12    execute immediate l_sql_stmt into l_cnt;
 13* end;
SQL> /

PL/SQL procedure successfully completed.
1 голос
/ 14 марта 2012

Лучшее решение, которое не требует временных таблиц, может заключаться в том, чтобы поместить данные в таблицу PL / SQL и затем присоединиться к ней. У Тома Кайта отличный пример: Пример объединения таблиц PL / SQL

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...