1

数据库

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 行。Table2 有 ~800k 行 Table1 有 ~200k 行

如果从查询中删除持续时间/24 计算,将其替换为静态值,则查询时间将减少一半。这不会检索完全相同的数据,但让我相信除法成本很高。

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

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

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

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

4

3 回答 3

3

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

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

应该选择查询的整个谓词上的复合索引,而单独索引优化器可能会决定基于对这些索引之一的扫描的 rowid 重复表访问实际上比全表扫描慢。

还要确保您没有从 varchar 到 date 进行隐式转换,方法是确保您在绑定变量中传递 DATE。

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

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

于 2009-06-02T18:16:19.240 回答
1

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

此外,当在 where 子句中修改索引列时,索引将被禁用。例如,UPPERCASE(some_index_column) 将禁用 some_index_column 上的索引。这就是 starttime + Duration/24 > :endtime 不使用索引的原因。

你能试试这个

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

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

于 2009-06-02T16:52:18.003 回答
1

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

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

对于“Starttime+duration/24”和 starttime 的复合索引,由于前导列是函数并且谓词“大于绑定值”,它将从索引的中途开始并一直工作到末尾。对于这些匹配中的每一个,它可以根据绑定值评估索引上的开始时间,并通过或拒绝该行。如果传入的结束日期是最近的,我怀疑这实际上涉及的索引数量要少得多。

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

“在 Sql Developer 中,查询计划显示没有使用索引。”如果没有使用索引来查找 table2 行,我怀疑优化器认为 table2 的大部分/全部将被返回 [显然不是,由你的号码]。我猜它虽然大部分 table1 会被返回,因此你的谓词都没有做很多过滤。正如我上面所说,我认为“小于”谓词不是选择性的,但“大于”应该是。看explain plan,尤其是ROWS值,看看Oracle是怎么想的

PS。调整值意味着优化器会更改其估计的基础。如果行程规划师说您的行程需要 6 小时,因为它假设平均速度为 50,如果您告诉它假设平均速度为 100,它会用 3 小时得出结果。它实际上不会影响您的旅行速度,也不会影响实际旅行所需的时间。因此,您只想更改该值以使其更准确地反映数据库(或会话)的实际值。

于 2009-06-03T03:44:26.717 回答