读懂索引的统计信息

索引只能帮助SQL Server找到符合条件的记录,它还需要知道每一种操作所要处理的数据量有多少,从而估算出复杂度,选取一个代价最小的执行计划。而如何获取准确的数据的分布信息,这就是索引的统计信息的作用了。

单列索引的统计信息

  • 以 dbo.OrderHeader 为例,这张表保存的是每张订单的概要信息,一张订单只会有一条记录,所以 OrderID 是不会重复的,假设目前有 31474 条记录。
IF OBJECT_ID('dbo.OrderHeader') IS NOT NULL
DROP TABLE dbo.OrderHeader
GO

CREATE TABLE dbo.OrderHeader ( OrderID int, InsertTime datetime default getdate() )
GO

DECLARE @i INT = 43659
DECLARE @j INT = 75132
WHILE @i <= @j
BEGIN
  INSERT INTO dbo.OrderHeader (OrderID) SELECT @i
  SET @i = @i + 1
END
create clustered index OrderHeader_CL on dbo.OrderHeader (OrderID)

dbcc show_statistics ("dbo.OrderHeader",OrderHeader_CL)

1. 统计头信息

  • 统计信息的头信息包含的信息比较多,下面只讲几个重要的指标。

列名 说明 示例
Name 统计信息对象的名称 OrderDetail_CL,也就是索引的名称
Updated 上一次更新统计信息的日期和时间 2026/05/13 3:05PM
Rows 表中的行数 31474,完全正确地反映了当前表里数据量
Rows Sampled 统计信息的抽样行数 31474,说明上次更新统计信息的时候对索引列字段全部扫了一遍
Steps 在统计信息的第三部分,会把数据分成几组 这里为 3 则直方图会有3组数据。该值最大为 200
Density 第一个列前缀的选择性 查询优化器不使用此值,纯粹是为了向后兼容
Average key length 所有键列的每个值的平均字节数 示例索引只有1列,类型是int,所以长度为4
String Index 如果为 YES 则统计信息中包含字符串摘要索引 示例索引列类型是int,所以为 NO
  • Updated 上一次更新统计信息的时间非常重要,根据它能够判断统计信息是什么时候更新的,是不是在数据量发生变化以后,是不是存在统计信息不能反映当前数据分布特点的问题。
  • 若 Rows Sampled < Rows 则直方图和密度信息是抽样估计的,若 Rows Sampled = Rows 则说明上次更新统计信息的时候,对所有记录的索引列字段都扫描了一遍,这样的统计信息一般都是很精确的。

2. 索引列的选择性

  • All density 反映索引列的选择性(Selectivity),它反映了数据集里重复的数据量多少,如果一个字段的数据很少有重复,那么它的可选择性就比较高。

注:如果 All density 小于0.1(越小越好),一般讲这个索引的选择性比较高。如果大于0.1,它的选择性就不高了。

列名 说明 示例
All density 1/非重复值,反映索引列的选择性,若数据很少重复则该值越小 示例数据均唯一,1/31474 即 3.177226E-05
Average Length 索引列的平均长度 示例索引只有1列,类型是int,所以长度为4
Columns 索引列的名称 示例索引创建在 OrderID 字段上

3. 直方图

  • 头信息中的 Steps 为 n,这里就有 n 组数据

  • 每组数据的总行数 = RANGE_ROWS + EQ_ROWS
列名 说明 示例
RANGE_HI_KEY 每一组(step)数据的最大值 示例分为3组:0 - 43659、43660 – 75131、75132 - 75132
RANGE_ROWS 每组数据区间的总行数,上限值除外 第1、第3组只有1个最大值,排除之后为0,
第2组则有31472行,排除最大值则为31471
EQ_ROWS 表中值与该组数据上限值相等的行数目 示例数据无重复,所以每组只有1个上限值
DISTINCT_RANGE_ROWS 每组数据区间非重复值的数目,上限值除外 示例数据无重复,所以非重复的行数 = RANGE_ROWS
AVG_RANGE_ROWS 每组数据区间重复值的平均数目,上限值除外 示例为1,计算公式=RANGE_ROWS/DISTINCT_RANGE_ROWS
  • 根据这个直方图可知:在 OrderID 字段上,最小值是 43659、最大值是 75132、共 31471+1+1+1 个值、且没有重复值

注:如果查询 OrderID 的条件在 43659 - 75132,那么 SQL Server 知道会返回一行,如果不在这个区间,就不会有返回,而且返回的每一行长度都是 4。所以通过统计信息,SQL Server 就能较好地预测返回的结果集的行数和长度。

组合索引的统计信息

  • 上面看的索引 OrderHeader_CL 数据分布比较简单,下面来看一个复杂的组合索引 SalesOrderDetail_test_NCL (SalesOrderID,SalesOrderDetailID)

注:SalesOrderDetail_test 的数据比较特别,前10%的数据,属于编号43659到75124这三万多条订单,而后90%的数据,平均属于75124到75132这9张订单

根据此索引的统计信息,分析如下:

  • 1、这里的数据分组(step)有 190 个,要详细很多
  • 2、在第二部分 Density,不但有索引列 SalesOrderID 的选择性值,还有 SalesOrderID + SalesOrderDetailID 合并起来的选择性值

注:可以看出如果同时使用两个字段进行过滤,其选择性(8.24E-07)会比只使用SalesOrderID(3.18E-05)还要高。

  • 3、SQL Server 会维护组合索引中所有字段的选择性信息,但直方图只有组合索引第一个字段 SalesOrderID 的信息,没有 SalesOrderDetailID 的信息
  • 4、从直方图的各项值分布情况,可以清楚地看出 SalesOrderDetail_test 这张表的数据分布特点。SQL Server能够根据带入的SalesOrderID值,推断出是只有几条、几十条、还是会有121317条记录返回,所以下面这两句话虽然结构一模一样,但是因为参数值不同,SQL Server选择了不同的执行计划。

场景1:当 SalesOrderID 在 43659 到 75123 之间,SQL Server “猜”到会返回 3 行(EstimateRows)。

select b.SalesOrderID, b.OrderDate, a.*
from dbo.SalesOrderDetail_test a 
inner join dbo.SalesOrderHeader_test b on a.SalesOrderID = b.SalesOrderID
where b.SalesOrderID = 72642

场景2:当 SalesOrderID 在 75124 到 75132 之间,SQL Server “猜”到会返回 121317 行(EstimateRows)。

select b.SalesOrderID, b.OrderDate, a.*
from dbo.SalesOrderDetail_test a 
inner join dbo.SalesOrderHeader_test b on a.SalesOrderID = b.SalesOrderID
where b.SalesOrderID = 75127

Copyright © www.sqlfans.cn 2021 - 2026 All Right Reserved更新时间: 2026-09-08 14:57:54

results matching ""

    No results matching ""