Почему я получаю сканирование индекса для покрытого запроса, используя функцию агрегирования? - PullRequest
2 голосов
/ 01 февраля 2011

У меня есть запрос:

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 |

Ответы [ 3 ]

4 голосов
/ 01 февраля 2011

Можете ли вы опубликовать фактический план запроса?Вы уверены, что он не выполняет полное / минимальное сканирование индекса?Как вы можете видеть в этом примере, мы получаем значение MIN из таблицы строк на 100 000, используя полное сканирование индекса min / max только с несколькими последовательными получениями.

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
3 голосов
/ 01 февраля 2011

Сначала я подумал, что индекс будет использоваться только в том случае, если столбец объявлен как 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 действительно выполняет "поиск".

1 голос
/ 25 сентября 2011

Просто хотел отточить тот факт, что «INDEX FULL SCAN» (MIN / MAX) просто не совпадает с «INDEX FULL SCAN».INDEX FULL SCAN действительно сканирует весь индекс (возможно, с фильтрацией).Однако, INDEX FULL SCAN (MIN / MAX) или INDEX RANGE SCAN (MIN / MAX) получает только самый маленький или самый большой листовой блок (из диапазона), но может использоваться только до тех пор, пока столбец NOT NULL (которыйнемного глупо и действительно является ошибкой, поскольку значение NULL по определению не является ни наименьшим, ни наибольшим значением).Оптимизация (MIN / MAX) является неявным действием FIRST_ROWS, и для ее выполнения не требуется условие запроса «WHERE ... IS NOT NULL».Интересно, что оптимизация MIN / MAX обычно не рассматривается CBO для индексов на основе функций, это еще одна маленькая ошибка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...