查询优化器如何使用统计—Part I

原文:http://sqlblog.com/blogs/ben_nevarez/archive/2009/09/03/how-the-query-optimizer-uses-statistics.aspx

备注:翻译不当,请指出或参考原文。

      本文介绍了查询优化器在优化时如何使用统计来估计表达式的选择性,与使用“本地变量”的方式猜测表达式的选择性相比,查询优化器采用探测参数值的方式略显一筹。

      注意:本文并未过多地介绍与之相关主题的其他细节,仅列举出一些简单的示例对查询优化器创建、使用统计进行描述。

 

       首先启动SQL Server Management Studio并切换到AdventureWorks数据库,运行以下命令显示Sales.SalesOrderDetail表中ProductID列的当前统计信息:

dbcc show_statistics('Sales.SalesOrderDetail', IX_SalesOrderDetail_ProductID)

该命令的输出结果显示了主题、密度及统计对象的histogram。

1)了解histogram

    首先介绍histogram步值的含义,先看步值为86的行,如下所示:

   clip_image002
   RANGE_HI_KEY:表示histogram 步的上边界,826是步值为85的上边界,而831是步值为86的上边界,也就是说,步值为86可能只包含从827到831之间的值。
 
运行下面的查询可以获取ProductID从827到831之间的记录个数:
select ProductID, COUNT(*) as Total 
from Sales.SalesOrderDetail
where ProductID between 827 and 831
group by ProductID
查询结果如下:
image
 
回到刚才的统计信息处,EQ_ROWS表示估计满足RANGE_HI_KEY值的记录个数,在本例中,RANGE_HI_KEY为831,ProductID为831的记录个数为198条,这与先前输出的统计信息一致。
 
RANGE_ROWS表示在827和830之间的行数(不包括上边界为831),以本例为例,记录个数包括从827到830之间的,histogram显示出有110条记录满足,通过先前执行的查询统计也可以算出。
 
DISTINCT_RANGE_ROWS表示在827和830之间不重复的行数(不包括上边界为831),以本例为例,有3个值:827,828,830,故DISTINCT_RANGE_ROWS为3。
 
AVG_RANGE_ROWS表示由RANGE_ROWS/DISTINCT_RANGE_ROWS计算出,以本例为例,即110/3=36.6667。
 
接下来,我们看一下统计如何用于估计查询的选择性。
 

2)查询优化器知道值

 
     看一下下面的查询:
select * from Sales.SalesOrderDetail 
where ProductID = 831

clip_image006
 
由于831为步值86的RANGE_HI_KEY,查询优化器使用EQ_ROWS,估计出的行数为198。
 
现在再运行上面的查询,参数值为828:
select * from Sales.SalesOrderDetail 
where ProductID = 828

clip_image008

这一次,这个值在步值为86的范围内,并且828不是RANGE_HI_KEY,故查询优化器使用AVG_RANGE_ROWS计算的列,其实,查询ProductID为829的记录,估计的行数也是36.6667,即使没有满足829条件的记录也是如此。

 

下面我们再来看一个不等的运算,试一下找出小于714的行数,也可以通过步值1到7的RANGE_ROWS和EQ_ROWS计算出(13223行)

image

下面看一下在执行计划中显示的估计行数:

select * from Sales.SalesOrderDetail 
where ProductID < 714
image
 
3)查询优化器对值未知
 
     这种情况下查询优化器对表达式中的值未知,像使用“本地变量”,由于查询优化器不能使用histogram,因此它会使用包括统计密度信息的其他信息或尝试使用一些固定百分比来猜测选择性,还是通过例子来说明,先看一个“相等”运算:
declare @pid int = 897 
select * from Sales.SalesOrderDetail
where ProductID = @pid

显然在上面的查询中,查询优化器对参数值897未知,由于查询优化器在优化时不知道@pid本地变量的值,故会使用ProductID列的密度值(0.003759399)作来参考,如果用总记录数(121317)乘以密度值(0.003759399),结果为456.079,此值与执行计划中显示的一致。
image
下面,再来看一下“不等”的运算:
declare @pid int = 897 
select * from Sales.SalesOrderDetail
where ProductID < @pid

与先前一样,此处与897无关,使用任何值都将得到相同的估计个数,对于不等运算来说,估计的行数约为总行数的30%,在本例为即为

121317*30%=36395.1。

image

posted @ 2011-10-19 12:00  Yuejun Sun  阅读(493)  评论(0编辑  收藏  举报