SQL查询用于全表扫描而不是基于索引的扫描

时间:2011-03-10 04:32:33

标签: sql oracle indexing

我有两张桌子:

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 个答案:

答案 0 :(得分:3)

不,不,不。

您不希望它使用索引。幸运的是,Oracle比这更聪明。

ID是数字。虽然它的ID值可能为45,450,451,452,4501,45004,4500003等,但在索引中这些值将分散在任何地方。如果您使用ID BETWEEN 450和459之类的条件,则可能值得使用索引。

要使用索引,必须从上到下一直扫描它(将每个ID转换为字符以进行LIKE比较)。然后,对于任何匹配,必须关闭以获取NAME列。

它已经决定扫描表格更容易,更快速(无论如何,75,000行不是那么大),而不是在索引和表格之间来回徘徊。

答案 1 :(得分:1)

其他人是对的,你不应该使用这样的数字列。

然而,实际上,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

答案 2 :(得分:-1)

这样的事可能有用:

select * 
  from big_table big
 where id like '45%' 
    or exists ( select id from small_table where id = big.id);