Использование Oracle Index в представлении с агрегатами

Вот фон:

Версия: 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. Я просто не могу понять, как его заставить. Я уверен, что это я, а не Оракул, просто я его не вижу.

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


person Community    schedule 17.09.2009    source источник


Ответы (3)


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

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

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

Я думаю, что вам также нужно ВЫЧИСЛИТЬ СТАТИСТИКУ в 8i.

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

person APC    schedule 18.09.2009
comment
Ты прав! Единственный реальный вопрос заключался в том, почему он не использовал мой функциональный индекс, как только я понял, что, вставив агрегат в представление, я больше не могу использовать индексы для базовых значений. Эти секретные заклинания Oracle высвободили мощь функциональных индексов, и я очень доволен. Спасибо! - person ; 21.09.2009

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

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

Создайте индекс для 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
person Quassnoi    schedule 17.09.2009
comment
Верно, потому что это агрегация, которую я хочу. Я переместил его вниз в представление, чтобы оптимизатор понял, что индекс все еще можно использовать. Вы говорите, что мой запрос должен быть SQL> выберите * из представления 2, где trunc (день, «ЧЧ») между 3 to_date («14 июня 2009 г. 0», «дд-мон-гггг чч24») и 4 to_date (' 14 июня 2009 г. 13','дд-мон-гггг чч24'); или что я просто не могу сделать представление, которое будет работать? Спасибо!!! - person ; 17.09.2009
comment
@Ian: да. В этом случае day должен быть исходным day, возвращенным из таблицы. - person Quassnoi; 17.09.2009
comment
Я создал именно этот индекс, но он не использовался. Даже с намеком! Включение DAY в запрос приводит к тому, что он не группируется по усеченному DAY. Ваш пример не скомпилируется, так как DAY не включен в предложение GROUP BY. - person ; 17.09.2009
comment
@Ian: правильно, я не заметил GROUP BY в вашем запросе. Смотрите это обновление поста. - person Quassnoi; 17.09.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'));
person Tony Andrews    schedule 17.09.2009
comment
@Tony: я тоже думал об этом, но он не скомпилируется: день не является агрегатом в вашем операторе создания представления. - person Vincent Malgrat; 17.09.2009
comment
Да, я тоже. Проблема в том, что я хочу, чтобы данные агрегировались по часам, включая неусеченный день в запросе, требующий включения его в предложение group by. - person ; 17.09.2009