2011-03-10 69 views
2

我有两个表:SQL查询去为全表扫描,而不是基于索引扫描

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) 

是否有任何方法可以重写查询以便始终进行索引扫描。

+1

那是一个错字 - 觉得这一定是要在其数据类型的列使用这样的错误是不 – 2011-03-10 04:59:53

+0

相关的字符串(VARCHAR2等)所以你想要以45开头的ID?像45,45029和451? – 2011-03-10 05:48:14

+0

是的,你是对的 – Ajitesh 2011-03-10 10:21:35

回答

-1

像这样的东西可能会奏效:

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

嗨,苏尼尔我试过,但得到了相同的结果执行计划 | Id |操作|名称|行|字节|成本(%CPU)|时间| ------------------------------------------------- --------------------------- | 0 | SELECT语句| | 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 |小| 1 | 4 | 3(0)| 00:00:01 | – Ajitesh 2011-03-10 10:23:34

3

没有,没有,没有。

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

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

要使用索引,它必须从上到下扫描它(将每个ID转换为一个字符来执行LIKE比较)。那么,对于任何匹配,它必须去获得NAME列。

它已经决定扫描表格(75000行并不是那么大),而不是在索引和表格之间来回扫描更容易和更快。

+2

现货。这样做的方式将是一个基于功能的索引,将ID转换为字符串。然后,由于缺少更好的术语,LIKE会表现“正确”。 – lll 2011-03-10 06:50:15

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