带有聚合的Oracle索引使用情况

时间:2009-09-17 15:13:20

标签: sql oracle optimization plsql indexing

以下是背景资料:

版本: 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 个答案:

答案 0 :(得分:2)

您的视图查询会在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

答案 1 :(得分:1)

在第一种情况下,WHERE子句中的“day”引用表列“day”,而不是查询结果列“day”,因此可以使用索引,但结果不包括14-jun的数据-2009 13:00:01起。

在第二种情况下,WHERE子句中的“day”引用视图列“day”,其定义为TRUNC(day,'HH')。所以这不能使用索引,而确实包含14-jun-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子句中。

因此,正如其他人已经建议的那样,最好坚持使用原始视图和查询,并添加基于函数的索引(FBI),如下所示:

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

答案 2 :(得分:0)

构建基于函数的索引ON IDcpdata (TRUNC(day, 'HH'))的建议是合理的。你有其他基于功能的索引吗?如果没有,这可能解释了为什么优化器不使用它。

这个索引类型是在8i中引入的,因此实现起来有点笨拙。具体而言,您需要设置一些数据库参数,否则优化程序会忽略索引。

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

我认为你还需要8i中的COMPUTE STATISTICS。

(我感谢谷歌和蒂姆霍尔的Oracle-Base site代表我失败的记忆。