2009-09-17 4 views
1

Вот фон:Oracle Index Использование в View с агрегатами

Версия: 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'); 

Когда я создаю представление на основе этого запроса, только без ИНЕК, запрос к этой точке зрения, с ИНЕК, не в состоянии использовать вид. Существует очень избирательный индекс, который используется в прямой версии 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 должен иметь возможность использовать индекс, так как inline SQL делает. Я просто не могу понять, как это сделать. Я уверен, что это я, а не Oracle, я просто этого не вижу.

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

ответ

0

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

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

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

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

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

+0

Вы правы! Единственный реальный вопрос: почему он не использовал мой функциональный индекс, как только я понял, что, нажимая агрегат в представление, я больше не мог использовать индексы для базовых значений. Эти секретные заклинания Oracle развязали мощь функциональных индексов, и я очень доволен. Спасибо! – 2009-09-21 18:30:59

2

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

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

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

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

Update:

Это работает на моем 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 
+0

Правда, потому что это агрегирование, которое я хочу. Я переместил его в представление, поэтому я подумал, что оптимизатор выяснит, что индекс все еще можно использовать. Вы говорите, мой запрос должен быть SQL> SELECT * FROM theview 2 где TRUNC (день, 'HH') между 3 to_date ('14 -jun-2009 0' , 'дд-пн-гггг HH24') и 4 to_date ('14 -jun-2009 13 ',' dd-mon-yyyy hh24 '); или что я просто не могу сделать вид, который будет работать? Спасибо !!! – 2009-09-17 15:27:36

+0

'@ Ian': да. В этом случае «день» должен быть оригинальным «днем», возвращаемым из таблицы. – Quassnoi

+0

Я создал этот точный индекс, но он не использовался. Даже с намеком! Включение DAY в запрос приводит к тому, что он не сгруппирован по усеченному Дню. Ваш пример не будет компилироваться, поскольку DAY не включен в предложение GROUP BY. – 2009-09-17 15:46:55

1

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

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

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

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.

Таким образом, как и другие уже предложили, то лучше придерживаться первоначального вида и запроса, а также добавить на основе индекса функции (ФБР), как это:

create index IDcpdata_truncday_idx ON IDcpdata (TRUNC(day,'HH')); 
+0

@ Тони: Я тоже думал об этом, но он не будет компилироваться: день не является совокупностью в вашем заявлении о создании. –

+0

Да, я тоже. Проблема заключается в том, что я хочу, чтобы агрегированные по часам данные, включая не усеченный день в запросе, включают в себя это в предложении group by. – 2009-09-17 15:45:26