【问题标题】:Variable vs string literal speed disparity变量与字符串文字速度差异
【发布时间】:2016-06-29 21:59:33
【问题描述】:

当我在WHERE 子句中使用字符串文字时,我有一个运行非常快(大约 1 秒)的简单查询,例如:

select *
from table
where theDate >= '6/5/2016'

但是,这个查询(没有本质上的不同)运行时间超过 2 分钟:

declare @thisDate as date
set @thisDate = '6/5/2016'

select *
from table
where theDate >= @thisDate

我尝试按照here 的建议使用OPTION(RECOMPILE),但它对提高性能没有任何帮助。 theDate 列是日期时间,此数据库是最近从 SQL Server 2005 迁移而来的。

我的theDate 列上没有索引,并且该表有超过 10 亿行。这是一个共享资源,我不想在没有确保它会有所帮助的情况下开始索引。

我发现使用逻辑而不是变量可以提供与字符串文字相同的性能:

select *
from table
where theDate >= dateadd(dd, -23, getdate())

但是,如果我用可变整数替换日期整数,性能又会受到阻碍。

如何包含变量并保持性能?

编辑

请求中包含的实际查询:

DECLARE @days INT

Set @days = 7

select c.DEBT_KEY
       , c.new_value
       , c.CHANGE_DATE
from changes c with (nolock)
where c.C_CODE = 3
and c.old_value = 4
and c.CHANGE_DATE >= dateadd(dd, -@days, getdate())

没有连接。

查询计划

带变量 (xml explain plan):

使用字符串文字(xml explain plan):

所以我可以看到不同之处在于变量调用聚集索引扫描(聚集),而字符串文字调用键查找(聚集)......我需要参考谷歌,因为我什么都不知道关于这些的性能优缺点。

编辑编辑

这有效(xml explain plan):

DECLARE @days INT

Set @days = 7

select c.DEBT_KEY
       , c.new_value
       , c.CHANGE_DATE
from changes c with (nolock)
where c.CHANGE_CODE = 3
and c.old_value = 4
and c.CHANGE_DATE >= dateadd(dd, -@days, getdate())
OPTION(OPTIMIZE FOR (@days = 7))

...我不知道为什么。我也不喜欢这个解决方案,因为它否定了我使用变量的目的,即把所有变量放在 proc 的顶部,以减少在不可避免的维护期间在代码中四处寻找的需要。

【问题讨论】:

  • 超过 10 亿行且没有索引 - 哇!搜索这批文字也需要很长时间......我假设还有某种缓存结果......你比较了执行计划吗? * 我不想在没有确定会有所帮助的情况下开始编制索引* 好吧,我可以向您保证,索引会有所帮助:-)
  • 嗯,有一个索引,但在theDate 上没有,我想我可以更清楚一点:P
  • 有这么多的行,你应该在你想在排序、过滤或连接操作中使用的所有列上使用索引... 索引 - 简单地说 - 该列值的排序列表。在这个列表中找到一个给定的值是一个非常快的过程(分成两半,看看是大是小,啊,它是更大的!划分上半部分等等......)一旦找到,所有的值都坐在一起堵塞。现在想象一个未排序的堆。您的查询必须逐行扫描整个表格。
  • 您的第一站应该是实际的查询计划。 mssqltips.com/sqlservertutorial/2252/…。任何其他分析都会使缓存问题变得模糊。因此,获取快速和慢速查询的实际/估计查询计划并进行比较。顺便说一句,“我不想在没有保证它会有所帮助的情况下开始索引”的态度是一种 优秀 方法 - 除非您了解问题并捕捉到一些问题,否则不要只是将索引扔给它测量前后
  • Google 的“参数嗅探”

标签: sql sql-server performance sql-server-2014


【解决方案1】:

快速版本执行聚集键查找(可以直接到找到该值的表部分)。

慢速版本执行非聚集查找,然后将其与聚集索引扫描合并(它必须扫描整个表)。

我看到@thisDate 变量被定义为Date 类型。该列是否可以定义为DateTime?如果这是真的,@thisDate 中的任何值都不会与您的聚集索引完全一致,这意味着数据库必须检查整个表,正如我们在此处看到的那样。另一方面,文字将被解释为DateTime 值,它确实 匹配您的表列类型并且将与索引一起使用。

如果这是正确的,您可以通过非常简单的更改来解决问题:

declare @thisDate as datetime
set @thisDate = '6/5/2016'

只有 4 个字符的区别。

您也可以将其与OPTION(RECOMPILE) 结合使用,您可能还想看看OPTIMIZE FOR UNKNOWN

【讨论】:

  • cmets 中建议的,不会加快查询速度。大约 8000 行在 2 分钟内返回。
猜你喜欢
  • 1970-01-01
  • 2013-05-19
  • 2015-02-07
  • 2010-09-06
  • 2018-12-30
  • 1970-01-01
  • 1970-01-01
  • 2017-04-10
相关资源
最近更新 更多