Запрос SQL идет для полного сканирования таблицы вместо сканирования на основе индекса - PullRequest
2 голосов
/ 10 марта 2011

У меня есть две таблицы:

create table big( id number, name varchar2(100));
insert into big(id, name) select rownum, object_name from all_objects;

create table small as select id from big where rownum < 10;
create index big_index on big(id);

В этих таблицах, если я выполняю следующий запрос:

select * 
  from big_table 
 where id like '45%' 
    or id in ( select id from small_table);

, он всегда выполняется для полного сканирования таблицы.

Execution Plan
----------------------------------------------------------
Plan hash value: 2290496975
----------------------------------------------------------------------------
| Id  | Operation          | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |  3737 | 97162 |    85   (3)| 00:00:02 |
|*  1 |  FILTER            |       |       |       |            |          |
|   2 |   TABLE ACCESS FULL| BIG   | 74718 |  1897K|    85   (3)| 00:00:02 |
|*  3 |   TABLE ACCESS FULL| SMALL |     1 |     4 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("ID"=45 OR  EXISTS (SELECT /*+ */ 0 FROM "SMALL" "SMALL"

          WHERE "ID"=:B1))

3 - filter("ID"=:B1)

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

Ответы [ 3 ]

3 голосов
/ 10 марта 2011

Нет, нет и нет.

Вы НЕ хотите, чтобы он использовал индекс. К счастью, Оракул умнее этого.

ID числовой. Хотя он может иметь значения идентификаторов 45,450,451,452,4501,45004,4500003 и т. Д., В индексах эти значения будут разбросаны везде и всюду. Если вы применили такое условие, как идентификатор между 450 и 459, то, возможно, стоит использовать индекс.

Чтобы использовать индекс, ему нужно будет сканировать его сверху вниз (преобразование каждого идентификатора в символ для сравнения LIKE). Затем для любого совпадения он должен выйти, чтобы получить столбец ИМЯ.

Было решено, что проще и быстрее сканировать таблицу (которая в любом случае с 75 000 строк не так уж и велика), а не копаться в поиске между индексом и таблицей.

1 голос
/ 10 марта 2011

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

Однако на самом деле именно конструкция OR <subquery> вызывает проблему (производительности) в этом случае.Я не знаю, если это отличается в версии 11, но до версии 10gr2, это вызывает операцию фильтра с тем, что в основном является вложенным циклом с коррелированным подзапросом.В вашем случае использование числового столбца в качестве varchar также приводит к полному сканированию таблицы.

Вы можете переписать свой запрос следующим образом:

select *
  from big
 where id like '45%'
union all
select *
  from big
  join small using(id)
 where id not like '45%';

В вашем тестовом примере яв итоге получим 174000 строк в большом и 9 маленьких.Выполнение вашего запроса занимает 7 секунд с 1211399 последовательных запросов.Выполнение моего запроса 0,7 секунды и использование 542 последовательных запросов.

Планы объяснения для моего запроса:

 --------------------------------------------------------------------
| Id  | Operation                     | Name   | Rows  | Cost (%CPU)|
---------------------------------------------------------------------
|   0 | SELECT STATEMENT              |        |  8604 |   154   (6)|
|   1 |  UNION-ALL                    |        |       |            |
|*  2 |   TABLE ACCESS FULL           | BIG    |  8603 |   151   (4)|
|   3 |   NESTED LOOPS                |        |     1 |     3   (0)|
|*  4 |    TABLE ACCESS FULL          | SMALL  |     1 |     3   (0)|
|   5 |    TABLE ACCESS BY INDEX ROWID| BIG    |     1 |     0   (0)|
|*  6 |     INDEX UNIQUE SCAN         | BIG_PK |     1 |     0   (0)|
---------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter(TO_CHAR("ID") LIKE '45%')
   4 - filter(TO_CHAR("SMALL"."ID") NOT LIKE '45%')
   6 - access("BIG"."ID"="SMALL"."ID")


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
        542  consistent gets
          0  physical reads
          0  redo size
      33476  bytes sent via SQL*Net to client
        753  bytes received via SQL*Net from client
         76  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       1120  rows processed
0 голосов
/ 10 марта 2011

Примерно так может работать:

select * 
  from big_table big
 where id like '45%' 
    or exists ( select id from small_table where id = big.id);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...