Oracle优化查询涉及日期计算

时间:2009-06-02 16:31:39

标签: oracle query-optimization

数据库

Table1
 Id
 Table2Id

...

Table2
  Id
  StartTime
  Duration  //in hours

查询

select * from Table1 join Table2 on Table2Id = Table2.Id 
where starttime < :starttime and starttime + Duration/24 > :endtime

此查询目前需要大约2秒才能运行,这太长了。 id列上有一个索引,Start_time上有一个函数索引+ duration / 24在Sql Developer中,查询计划显示没有使用索引。查询为我的测试开始和结束时间返回475行。表2有~800k行Table1有~200k行

如果从查询中删除持续时间/ 24计算,则用静态值替换查询时间减半。这不会检索完全相同的数据,但让我相信该部门很昂贵。

我还测试过将一个endtime列添加到Table2,其中填充了(starttime + duration / 24)该列是通过单个更新预先填充的,如果它将在生产中使用,我将通过更新触发器填充它。

select * from Table1 join Table2 on Table2Id = Table2.Id 
where starttime < :starttime and endtime > :endtime

此查询将在大约600毫秒内运行,并使用索引进行连接。由于带有冗余数据的附加列,它不太理想。

有没有什么方法可以让这个查询更快?

3 个答案:

答案 0 :(得分:3)

在starttime和表达式starttime + Duration/24上创建一个函数索引:

create index myindex on table2(starttime, starttime + Duration / 24);

应该选择查询的整个谓词的复合索引,而单独索引优化器可能会决定基于对其中一个索引的扫描而对rowid进行的重复表访问实际上比完整表扫描慢。 / p>

通过确保在绑定变量中传递DATE,确保您没有从varchar进行隐式转换。

尝试降低optimizer_index_cost_adj系统参数。我相信默认值为100.尝试将其设置为10并查看您的索引是否已被选中。

考虑按开始时间对表进行分区。

答案 1 :(得分:1)

如果where子句的选择性不是很好,Oracle就不会使用索引。如果返回的行数将占表中行总数的一定百分比,则将使用索引(百分比会有所不同,因为oracle将计算读取索引以及读取表的成本)。

此外,在where子句中修改索引列时,将禁用索引。例如,UPPERCASE(some_index_column)将禁用some_index_column上索引的使用。这就是为什么starttime + Duration / 24&gt; :endtime不使用索引。

你能试试吗

select * from Table1 join Table2 on Table1.Id = Table2.Table1Id 
where starttime < :starttime and starttime  > :endtime - Duration/24

这应该允许使用索引,而不需要额外的列。

答案 2 :(得分:1)

您有两个范围谓词(大于/小于)的条件。索引范围扫描可以从索引中的一个点开始,然后在另一个点结束。

对于starttime上的复合索引和“Starttime + duration / 24”,由于前导列是starttime而谓词是“小于绑定值”,它将从索引的最左边开始(最早的starttime)和范围扫描所有行直到启动时间达到限制的点。对于每个匹配,它可以针对绑定值评估索引的“Starttime + duration / 24”的计算值,并传递或拒绝该行。我怀疑表中的大多数数据都是旧的,因此大多数条目都有一个旧的开始时间,你最终会扫描大部分索引。

对于“Starttime + duration / 24”和starttime的复合索引,由于前导列是函数而谓词是“大于bindvalue”,因此它将从索引的中途开始并一直运行到最后。对于每个匹配,它可以针对绑定值评估索引的开始时间,并传递或拒绝该行。如果传入的enddate是最近的,我怀疑这实际上涉及的扫描索引数量要少得多。

即使没有将starttime作为索引的第二列,基于“Starttime + duration / 24”的现有基于函数的索引仍应有用并使用。检查解释计划以确保bindvalue是日期或转换为日期。如果转换,请确保使用适当的格式掩码(例如,'1 / Jun / 09'的输入值可能会转换为0009年,因此Oracle会将条件视为非常放松并且不会使用索引 - 加上结果可能是错误的。)

“在Sql Developer中,查询计划显示没有使用索引。”如果索引没有用于查找table2行,我怀疑优化器认为table2的大部分/全部都会被返回[显然它不是' t,按你的数字]。我猜它虽然会返回大部分table1,因此你的谓词都没有做过大量的过滤。正如我上面所说的,我认为“小于”谓词不是选择性的,但“大于”应该是。查看解释计划,尤其是ROWS值,以了解Oracle的想法

PS。 调整值意味着优化程序会更改其估计值的基础。如果一个旅程计划者说你要花六个小时的旅行,因为它假设平均速度为50,如果你告诉它假设平均为100,它将会有三个小时。它实际上不会影响你旅行的速度,或实际旅行所需的时间。 因此,您只想更改该值,以使其更准确地反映数据库(或会话)的实际值。

相关问题