2011-02-01 21 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优化,因为它是基于函数的索引,这是另一个小错误。

相关问题