2011-03-10 89 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