【问题标题】:Losing indexing and performance when using SUBSTRB使用 SUBSTRB 时失去索引和性能
【发布时间】:2015-09-14 16:01:16
【问题描述】:

由于此参数查询,我的报告运行速度非常慢,因为我认为我在使用 SUBSTRB 时丢失了索引

select distinct SUBSTRB(BLD_YEAR_PERIOD,1,4) as BLD_YEAR_PERIOD
from example.table
where SUBSTRB(BLD_YEAR_PERIOD,1,4) >= 2010
and SUBSTRB(BLD_YEAR_PERIOD,5,2) = 01
order by BLD_YEAR_PERIOD

关于其他方式的任何建议。

【问题讨论】:

  • 比较快慢查询计划。有什么不同?是的,SUBSTRB 将停止使用BLD_YEAR_PERIOD 上的索引如果存在。你也有一些隐式转换。您应该比较字符串而不是数字。这也相当于 BLD_YEAR_PERIOD= '201001' 不需要该功能。
  • 是否有任何特殊原因您使用字节方式子字符串函数而不是普通的SUBSTR 函数?
  • 您已评论数据类型为数字。那你为什么不能用这个:where BLD_YEAR_PERIOD = 201001
  • 因为我在 SSRS 中使用它作为参数来选择多个日期,所以我想要每个以 01 结尾的不同日期选项
  • 我只是注意到年份过滤器不是一个相等谓词。基于函数的索引可能仍然有帮助,但这里的一个重要问题是,该查询通常返回的表中行的百分比大约是多少?如果它超过表的 5%,我通常希望 Oracle 无论如何都会进行完整扫描。

标签: sql oracle indexing substring


【解决方案1】:

是的,在列上调用像 SUBSTRB 这样的函数通常会导致 Oracle 避免在该列上使用普通索引。

如果您的查询确实具有代表性,并且您正在使用给定的确切参数搜索列,您可能会发现使用了基于函数的索引并且可能使查询更有效率,例如:

CREATE INDEX idx1 ON example.table (
  SUBSTRB(BLD_YEAR_PERIOD,1,4)
 ,SUBSTRB(BLD_YEAR_PERIOD,5,2));

覆盖索引可能使其更加高效,假设您给出的查询是正在运行的实际查询

CREATE INDEX idx1 ON example.table (
  SUBSTRB(BLD_YEAR_PERIOD,1,4)
 ,SUBSTRB(BLD_YEAR_PERIOD,5,2)
 ,BLD_YEAR_PERIOD);

我要检查的一件事是,为什么优化器尚未在BLD_YEAR_PERIOD 上的现有索引上使用快速全扫描(假设这是您现在所拥有的,并且假设这是您唯一的列)在您的查询中引用)。

【讨论】:

  • 如果 BLD_YEAR_PERIOD 字段上没有现有索引,就性能而言,我使用 SUBSTRB 函数是否重要?
  • 不,没关系。对于像您这样的查询要快速运行,主要考虑因素是 Oracle 可以用来快速减少搜索空间的 访问方法。如果没有索引可以做到这一点,它最快的方法是全表扫描。在这种情况下,您的谓词只是过滤谓词,对整体性能的影响可以忽略不计。
【解决方案2】:

问题是你的s搜索arguments 不再是sargable。当您在表格中进行比较时:

where field = <something>

where field = func( <something> )

然后系统有一个静态值来与该字段进行比较。作为一个静态值,它不会从一行更改到下一行。如果该字段被索引,它可以快速扫描到该值所在的位置或快速确定该值是否不存在。

但是,如果比较的形式是:

where func( field ) = <something>

无法提前知道函数相对于字段值的结果。必须将每一行的字段值传递给函数,以便进行比较。所以,是的,即使该字段被索引,仍然必须进行完整的表扫描。

诀窍是重新进行比较,以便不会对字段进行任何操作。在你的情况下,这很容易。您要查找的是 2010 年 1 月发生的所有日期值。这可以这样表达:

where   BLD_YEAR_PERIOD >= date '2010-01-01' 
    and BLD_YEAR_PERIOD < date '2010-02-01'

如果您传入一个日期参数,并且您想要在该日期当月发生的所有结果,则可能是这样的:

where   BLD_YEAR_PERIOD >= trunc( p_date, 'month' )
    and BLD_YEAR_PERIOD < add_month( trunc( p_date, 'month' ), 1 )

这确保静态日期值从当月的第一天和下个月的第一天开始。现在可以使用索引了。

【讨论】:

  • 很好的建议和事实,除了 Oracle 支持基于函数的索引这一事实。
  • 是的,但您必须创建两个索引:一个用于 trunc,一个用于 add_month。如果其他查询对该字段或其他字段使用其他函数,则您必须为每个字段创建一个索引。如果可以确定您的所有查询都将使用原始字段值或一个函数的结果,那么就去做吧。尽管如此,知道如何编写可搜索查询总是好的。
  • 我想你假设 BLD_YEAR_PERIOD 是一个日期。 OP的问题中没有任何内容表明这一点,并且有很多暗示它是一个字符串,例如'201001' 2010 年 1 月。
  • 仔细想想,这对于使用从文本到日期的转换函数的基于函数的索引来说是一个很好的例子。然后在我的答案中使用 that 字段。
  • 一般来说,一个字段应该是与属性相同的数据类型。也就是说,如果属性是整数值,则将字段定义为整数(或 number(x,0))。如果属性是日期,则字段应该是日期。这有助于域定义和易于查询。在您的情况下,您仍然可以按原样使用这些值,但那是运气。永远不要依赖运气。
猜你喜欢
  • 2011-03-26
  • 1970-01-01
  • 1970-01-01
  • 2017-02-07
  • 1970-01-01
  • 2011-06-29
  • 2011-11-04
  • 2021-09-19
  • 2014-07-01
相关资源
最近更新 更多