以下是背景资料:
版本: 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可以。我只是无法弄清楚如何强迫它。我确定这是我,而不是甲骨文,我只是没有看到它。
提前感谢任何建议!
答案 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代表我失败的记忆。