需要帮助了解此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溢出?
答案 0 :(得分:1)
tempdb泄漏是因为估计,这是不对的,因为 我正在使用局部变量。
为什么因为局部变量而导致错误的一些内容:
SQL Server基数估计可以使用两种类型的统计信息来猜测通过谓词过滤器将获得多少行:
如果您不熟悉统计对象及其密度向量/直方图,请阅读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 used和queried 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对象,请告诉我,它不会像上面那样加起来。
答案 1 :(得分:0)
tempdb泄漏是因为估算,这是不对的,因为我使用的是局部变量。
如果我使用sp_executesql
将局部变量更改为参数化SQL,则估计值变得正确,并且tempdb溢出消失了。
但是,即使解决了tempdb溢出问题,参数化SQL 仍然比使用文字慢,我为这个单独的问题创建了一个新问题。
SQL Server 2014 performance - parameterized SQL vs. literals