【问题标题】:Operator used tempdb to spill.... with variables but not literals操作员使用 tempdb 溢出.... 有变量但没有文字
【发布时间】:2015-08-14 17:45:13
【问题描述】:

需要帮助了解此 SQL Server 行为

我有一个相当基本的查询,比如

select x, y, sum(z) 
from table 
where date between @start and @end
group by x, y

存在大量行(过滤条件从总共 1600 万行中检索到 600 万行)

我不明白的是:这个查询很慢,我收到一个关于溢出到 tempdb 的警告。但是,如果我更改它并直接将@start@end 替换为相同的日期,它会快得多,并且不会出现有关 tempdb 溢出的警告。

我的猜测是 tempdb 溢出是因为基数估计。

看来,当我使用变量时,统计数据很差。它估计大约 145 万行而不是 600 万行。

当我使用文字时,估计值几乎完全正确。

在使用变量时如何获得正确的估计值并避免 tempdb 溢出?

【问题讨论】:

  • 您的统计数据是最新的吗?
  • 我认为统计数据可以基于使用文字看到正确的估计。问题似乎是我的变量值没有用于估计。
  • 如果你这样做where date >= @start and date <= @end会发生什么?
  • 之间与不等式运算符的行为相同

标签: sql sql-server


【解决方案1】:

tempdb 溢出是因为估计,这是不正确的,因为 我使用的是局部变量。

关于为什么由于局部变量而出错的一些内容:

SQL Server 基数估计可以使用两种类型的统计信息来猜测有多少行将通过谓词过滤器:

  1. 使用密度向量对列进行平均统计,并且
  2. 使用直方图的该列的特定值的统计信息

如果您不熟悉统计对象及其密度向量/直方图,请阅读this

当使用文字时,基数估计器可以在直方图中搜索该文字(第二种统计类型)。使用参数时,直到基数估计之后才会评估其值,因此 CE 必须在密度向量中使用列平均值(第一种统计类型)。

一般来说,使用文字可以获得更好的估计,因为直方图中的统计信息是根据文字的值量身定制的,而不是在整个列上取平均值。


示例

案例 1:字面量

我正在AdventureWorks2012_Data 数据库上运行以下查询:

SELECT *
FROM Sales.SalesOrderDetail
WHERE UnitPriceDiscount = 0

我们有一个文字,因此 CE 将在 UnitPriceDiscount 直方图中查找值 0 以确定将返回多少行。

我有run debugging output to see which statistics object is being usedqueried that object to see its contents,这是直方图:

0 的值是一个 RANGE_HI_KEY,因此具有该值的估计行数是它的 EQ_ROWS 列 - 在本例中为 117996.9。

现在让我们看看查询的执行计划:

“过滤器”步骤正在删除所有与谓词不匹配的行,因此其属性的“估计行数”部分具有基数估计的结果:

这是我们在直方图中看到的值,四舍五入。

案例2:参数

现在我们将尝试使用一个参数:

DECLARE @temp int = 0

SELECT *
FROM Sales.SalesOrderDetail
WHERE UnitPriceDiscount = @temp

基数估计器没有要在直方图中搜索的文字,因此它必须使用来自密度向量的列的整体密度:

这个号码是:

1 / the number of distinct values in the UnitPriceDiscount column

因此,如果您将其乘以表中的行数,您将得到该列中每个值的平均行数。 Sales.SalesOrderDetail中有121317行,所以计算是:

121317 * 0.1111111 = 13479.6653187

执行计划:

过滤器的属性:

所以新的估计来自密度向量,而不是直方图

如果您查看了 stats 对象并且它没有像上面那样加起来,请告诉我。

【讨论】:

    【解决方案2】:

    tempdb 溢出是因为估计,这是不正确的,因为我使用的是局部变量。

    如果我使用 sp_executesql 将局部变量更改为参数化 SQL,则估计值变得正确,并且 tempdb 溢出消失了。

    但是,即使解决了 tempdb 溢出问题,参数化 SQL 仍然比使用文字慢,我为这个单独的问题创建了一个新问题。

    SQL Server 2014 performance - parameterized SQL vs. literals

    【讨论】:

    • 所以把它包在 sp_executesql 中是你的解决方法吗?好奇,想知道计划缓存是否在这里发挥了作用。我很好奇像 Gordon Linoff 这样的人在这里说了什么。谢谢。
    • 我不认为计划缓存是一个因素,因为我没有使用任何 other 值运行相同的查询。
    • 更有可能使用局部变量是“特殊的” - 找到此链接brentozar.com/archive/2015/03/…
    猜你喜欢
    • 2021-10-16
    • 1970-01-01
    • 1970-01-01
    • 2015-03-31
    • 2015-08-08
    • 1970-01-01
    • 1970-01-01
    • 2016-01-15
    • 1970-01-01
    相关资源
    最近更新 更多