2011-02-01 86 views
2

我有一個查詢:爲什麼我使用聚合函數獲得覆蓋查詢的索引掃描?

select min(timestamp) from table 

這個表有60個多萬行,每天我刪除幾關結束。要確定是否有足夠的數據刪除,我運行上面的查詢。在時間戳升序上有一個索引,只包含一列,而oracle中的查詢計劃會導致它成爲完整的索引掃描。這不應該是尋求的定義嗎?

編輯,包括計劃:

| Id | Operation     | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
| 2 | INDEX FULL SCAN (MIN/MAX)| NEVENTS_I2 |  1 |  8 |  4 (100)| 00:00:01 | 
| 1 | SORT AGGREGATE   |   |  1 |  8 |   |   | 
| 0 | SELECT STATEMENT   |   |  1 |  8 |  4 (0)| 00:00:01 | 
+0

通過「全面掃描」我假設你的意思是索引全掃描,而不是表掃描,正確的? – tbone 2011-02-01 16:57:22

+0

@tbone是一個完整的索引掃描 – 2011-02-01 17:02:18

回答

3

起初,我以爲,如果列聲明NOT NULL該指數將僅被使用。我測試了以下設置:

SQL> CREATE TABLE my_table (ts TIMESTAMP); 

Table created 

SQL> INSERT INTO my_table 
    2 SELECT systimestamp + ROWNUM * INTERVAL '1' SECOND 
    3 FROM dual CONNECT BY LEVEL <= 100000; 

100000 rows inserted 

SQL> CREATE INDEX ix ON my_table(ts); 

Index created 

SQL> EXPLAIN PLAN FOR SELECT MIN(ts) FROM my_table; 

Explained 

SQL> SELECT * FROM TABLE(dbms_xplan.display); 

-------------------------------------------------------------------------------- 
| Id | Operation     | Name | Rows | Bytes | Cost (%CPU)| Time 
-------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT   |  |  1 | 13 | 69 (2)| 00:00:0 
| 1 | SORT AGGREGATE   |  |  1 | 13 |   | 
| 2 | INDEX FULL SCAN (MIN/MAX)| IX | 90958 | 1154K|   | 
-------------------------------------------------------------------------------- 

這裏我們注意到使用了索引,但索引中的所有行都被讀取。如果我們指定的列不爲空,我們得到一個更好的計劃:

SQL> ALTER TABLE my_table MODIFY ts NOT NULL; 

Table altered 

SQL> EXPLAIN PLAN FOR SELECT MIN(ts) FROM my_table; 

Explained 

SQL> SELECT * FROM TABLE(dbms_xplan.display); 

-------------------------------------------------------------------------------- 
| Id | Operation     | Name | Rows | Bytes | Cost (%CPU)| Time 
-------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT   |  |  1 | 13 |  2 (0)| 00:00:0 
| 1 | SORT AGGREGATE   |  |  1 | 13 |   | 
| 2 | INDEX FULL SCAN (MIN/MAX)| IX | 90958 | 1154K|  2 (0)| 00:00:0 
-------------------------------------------------------------------------------- 

事實上,這是也使用,如果我們增加一個WHERE子句相同計劃(Oracle會從索引讀取單行):

SQL> EXPLAIN PLAN FOR SELECT MIN(ts) FROM my_table WHERE ts IS NOT NULL; 

Explained 

SQL> SELECT * FROM TABLE(dbms_xplan.display); 

-------------------------------------------------------------------------------- 
| Id | Operation     | Name | Rows | Bytes | Cost (%CPU)| Time 
-------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT   |  |  1 | 13 |  2 (0)| 00:00: 
| 1 | SORT AGGREGATE    |  |  1 | 13 |   | 
| 2 | FIRST ROW     |  | 90958 | 1154K|  2 (0)| 00:00: 
| 3 | INDEX FULL SCAN (MIN/MAX)| IX | 90958 | 1154K|  2 (0)| 00:00: 
-------------------------------------------------------------------------------- 

最後一個計劃顯示(第2行)Oracle確實在執行「seek」操作。

4

你能發佈實際的查詢計劃嗎?你確定它沒有進行最小/最大索引全面掃描嗎?正如你在這個例子中看到的那樣,我們使用最小/最大索引全掃描從一個100,000行表中獲得最小值,只有少數一致性得到。

SQL> create table foo (
    2 col1 date not null 
    3 ); 

Table created. 

SQL> insert into foo 
    2 select sysdate + level 
    3  from dual 
    4 connect by level <= 100000; 

100000 rows created. 

SQL> create index idx_foo_col1 
    2  on foo(col1); 

Index created. 

SQL> analyze table foo compute statistics for all indexed columns; 

Table analyzed. 

SQL> set autotrace on; 

<<Note that I ran this statement once just to get the delayed block cleanout to 
    happen so that the consistent gets number wouldn't be skewed. You could run a 
    different query as well>> 

    1* select min(col1) from foo 
SQL>/

MIN(COL1) 
--------- 
02-FEB-11 


Execution Plan 
---------------------------------------------------------- 
Plan hash value: 817909383 

-------------------------------------------------------------------------------- 

----------- 

| Id | Operation     | Name   | Rows | Bytes | Cost (%CPU)| 

Time  | 

-------------------------------------------------------------------------------- 

----------- 

| 0 | SELECT STATEMENT   |    |  1 |  7 |  2 (0)| 

00:00:01 | 

| 1 | SORT AGGREGATE   |    |  1 |  7 |   | 

      | 

| 2 | INDEX FULL SCAN (MIN/MAX)| IDX_FOO_COL1 |  1 |  7 |  2 (0)| 

00:00:01 | 

-------------------------------------------------------------------------------- 

----------- 


Note 
----- 
    - dynamic sampling used for this statement (level=2) 


Statistics 
---------------------------------------------------------- 
      0 recursive calls 
      0 db block gets 
      2 consistent gets 
      0 physical reads 
      0 redo size 
     532 bytes sent via SQL*Net to client 
     524 bytes received via SQL*Net from client 
      2 SQL*Net roundtrips to/from client 
      0 sorts (memory) 
      0 sorts (disk) 
      1 rows processed 
1

只是想磨練一個事實,即一個「索引全掃描(MIN/MAX)」是根本不一樣的一個「索引全掃描」。 INDEX FULL SCAN確實會掃描整個索引(可能會進行篩選)。然而,INDEX FULL SCAN(MIN/MAX)或INDEX RANGE SCAN(MIN/MAX)只能得到最小或最大的葉塊(範圍內),但只能在列不爲NULL的情況下使用有點愚蠢,真的是一個錯誤,因爲NULL值根據定義既不是最小值也不是最大值)。 (MIN/MAX)優化是一個隱含的FIRST_ROWS操作,並且不需要「WHERE ... IS NOT NULL」查詢條件來執行優化。有趣的是,CBO通常不考慮MIN/MAX優化,因爲它是基於函數的索引,這是另一個小錯誤。

相關問題