Использование индекса Oracle с использованием агрегатов - PullRequest
1 голос
/ 17 сентября 2009

Вот фон:

Версия: Oracle 8i (не ненавидите меня за то, что я устарел. Мы обновляем!)

SQL> describe idcpdata
Name                                      Null?    Type
----------------------------------------- -------- ---------------------------

ID                                        NOT NULL NUMBER(9)
DAY                                       NOT NULL DATE
STONE                                              NUMBER(9,3)
SIMPSON                                            NUMBER(9,3)
OXYCHEM                                            NUMBER(9,3)
PRAXAIR                                            NUMBER(9,3)

Вот запрос, который сразу возвращается:

SQL> select to_char(trunc(day,'HH'),'DD-MON-YYYY HH24') day,
2  avg(decode(stone,-9999,null,stone)) stone,
3  avg(decode(simpson,-9999,null,simpson)) simpson,
4  avg(decode(oxychem,-9999,null,oxychem)) oxychem,
5  avg(decode(praxair,-9999,null,praxair)) praxair
6  from IDcpdata
7  where day between
8  to_date('14-jun-2009 0','dd-mon-yyyy hh24') and
9  to_date('14-jun-2009 13','dd-mon-yyyy hh24')
10  group by trunc(day,'HH');

Когда я создаю представление на основе этого запроса, только без предложения where, запрос к этому представлению с предложением where не может использовать представление. Существует высокоселективный индекс, который используется в версии прямого SQL-запроса. Полное сканирование таблицы занимает 20 минут.

create or replace view theview as
select TRUNC(day,'HH') day, 
avg(decode(stone,-9999,null,stone)) stone, 
avg(decode(simpson,-9999,null,simpson)) simpson, 
avg(decode(oxychem,-9999,null,oxychem)) oxychem, 
avg(decode(praxair,-9999,null,praxair)) praxair 
from IDcpdata group by TRUNC(day,'HH');


SQL> select * from theview
2  where day between
3  to_date('14-jun-2009 0','dd-mon-yyyy hh24') and
4  to_date('14-jun-2009 13','dd-mon-yyyy hh24');

Я попробовал подсказки INDEX () в представлении, запросе и в обоих случаях. Я попробовал глобальную подсказку INDEX, указав полное имя базовой таблицы. Я также попробовал MERGE.

Мне кажется, что Oracle должен иметь возможность использовать индекс, как это делает встроенный SQL. Я просто не могу понять, как заставить это. Я уверен, что это я, а не Оракул, я просто не вижу этого.

Заранее спасибо за любые предложения!

Ответы [ 3 ]

2 голосов
/ 17 сентября 2009

Ваш фильтр запросов просмотра TRUNC(day,'HH'), а не day.

Поскольку вы определили свое представление для возврата TRUNC(day,'HH') AS day, это усеченное значение дня, к которому применяется предложение BETWEEN, и оно не может быть sargable.

Создать индекс для TRUNC(day, 'HH'):

CREATE INDEX ix_idcpdata_truncday ON IDcpdata (TRUNC(day, 'HH'))

Обновление:

Это работает на моем Oracle 10g XE:

CREATE TABLE t_group (id INT NOT NULL PRIMARY KEY, day DATE NOT NULL)
/

INSERT
INTO    t_group
SELECT  level, TRUNC(SYSDATE) - level
FROM    dual
CONNECT BY
        level <= 100
/

CREATE INDEX ix_group_truncday ON t_group (TRUNC(day, 'HH'))
/

CREATE VIEW v_group AS
SELECT  TRUNC(day, 'HH') AS day
FROM    t_group
GROUP BY
        TRUNC(day, 'HH')
/

EXPLAIN PLAN FOR
SELECT  *
FROM    v_group
WHERE   day BETWEEN TO_DATE('01.08.2009', 'dd.mm.yyyy') AND TO_DATE('02.08.2009', 'dd.mm.yyyy')
/

SELECT  *
FROM    TABLE(DBMS_XPLAN.display)
/

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1656741214
--------------------------------------------------------------------------------
| Id  | Operation                    | Name              | Rows  | Bytes | Cost
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                   |     1 |     9 |     2
|   1 |  HASH GROUP BY               |                   |     1 |     9 |     2
|   2 |   TABLE ACCESS BY INDEX ROWID| T_GROUP           |     1 |     9 |     1
|*  3 |    INDEX RANGE SCAN          | IX_GROUP_TRUNCDAY |     1 |       |     1
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access(TRUNC(INTERNAL_FUNCTION("DAY"),'fmhh')>=TO_DATE('2009-08-01 00:00:
              'yyyy-mm-dd hh24:mi:ss') AND TRUNC(INTERNAL_FUNCTION("DAY"),'fmhh'
              00:00:00', 'yyyy-mm-dd hh24:mi:ss'))

17 rows selected
1 голос
/ 17 сентября 2009

В первом случае «день» в предложении WHERE ссылается на столбец таблицы «день», а не на столбец результата запроса «день», поэтому можно использовать индекс, но результаты не включают данные за 14 июня -2009 с 13:00:01 и далее.

Во втором случае «день» в предложении WHERE ссылается на столбец представления «день», который определяется как TRUNC (день, «ЧЧ»). Таким образом, он не может использовать индекс, а включает данные за 14 июня 2009 г. с 13:00:01, т. Е. Два запроса не эквивалентны.

Вы можете надеяться достичь наилучшего из обоих подходов, как это:

create or replace view theview as
select day,
TRUNC(day,'HH') trunc_day, 
avg(decode(stone,-9999,null,stone)) stone, 
avg(decode(simpson,-9999,null,simpson)) simpson, 
avg(decode(oxychem,-9999,null,oxychem)) oxychem, 
avg(decode(praxair,-9999,null,praxair)) praxair 
from IDcpdata group by TRUNC(day,'HH');

SQL> select trunc_day, stone, simpson, oxychem, pracair
2  from theview
3  where day >= to_date('14-jun-2009 0','dd-mon-yyyy hh24')
4  and day < to_date('14-jun-2009 13','dd-mon-yyyy hh24');

Однако, как отмечают комментарии ниже, это терпит неудачу, потому что день столбца отсутствует в предложении GROUP BY.

Поэтому, как уже предлагали другие, лучше придерживаться исходного представления и запроса и добавить индекс на основе функций (FBI), например:

create index IDcpdata_truncday_idx ON IDcpdata (TRUNC(day,'HH'));
0 голосов
/ 18 сентября 2009

Совет по созданию индекса на основе функций ON IDcpdata (TRUNC(day, 'HH')) обоснован. У вас есть другие функциональные индексы? Если нет, это может объяснить, почему оптимизатор не использует его.

Этот тип индекса был введен в 8i, и, следовательно, реализация была немного более неловкой. В частности, вам нужно установить некоторые параметры базы данных, в противном случае оптимизатор игнорирует индекс.

ALTER SESSION SET QUERY_REWRITE_INTEGRITY = TRUSTED; 
ALTER SESSION SET QUERY_REWRITE_ENABLED = TRUE;

Я думаю, вам также нужно вычислить статистику в 8i.

(Я в долгу перед Google и сайтом Тима Холла Oracle-Base , который заменяет мою память с ошибками).

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