我有两张桌子:
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)
我们是否有任何方法可以重写查询以便它始终用于索引扫描。
答案 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);