官方文档
https://learn.microsoft.com/zh-cn/sql/relational-databases/statistics/statistics?view=sql-server-ver16
When the automatic update statistics option, AUTO_UPDATE_STATISTICS is ON, the Query Optimizer determines when statistics might be out-of-date and then updates them when they’re used by a query. This action is also known as statistics recompilation. Statistics become out-of-date after modifications from insert, update, delete, or merge operations change the data distribution in the table or indexed view. The Query Optimizer determines when statistics might be out-of-date by counting the number of row modifications since the last statistics update and comparing the number of row modifications to a threshold. The threshold is based on the table cardinality, which can be defined as the number of rows in the table or indexed view.
在自动更新统计信息选项 AUTO_UPDATE_STATISTICS 为 ON 时,查询优化器将确定统计信息何时可能过期,然后在查询使用这些统计信息时更新它们。 此操作亦称为“统计信息重新编译”。 当插入、更新、删除或合并操作的修改更改了表或索引视图中的数据分布后,统计信息就会过时。 查询优化器通过计算自上次统计信息更新以来的行修改次数,并将行修改次数与阈值进行比较,来确定统计信息何时过时。 阈值基于表基数,表基数可以定义为表或索引视图中的行数。
无论表是临时的还是永久的,阈值是不同的。不过表的基数大于500行时,一般改动大于20%认为达到了统计信息过期的阀值,比如你的表包含 2 万行,则计算为 500 + (0.2 * 20,000) = 4,500,每4500次修改后,统计信息将更新一次。也就是说4500次修改后,认为这个表的相关统计信息过期了。
1、表的修改次数百分百达到20%后自动更新统计信息?
答案:我们跑一个语句的时候,不管是查询语句还是dml语句,只要这个语句开始准备跑了,就会触发查询优化器去查询这个语句涉及哪些统计信息,如果这些统计信息过期了,就更新。所以如果一张表有10万行,我们需要delete语句删除5万行,这个delete开始执行之前,查询优化器会去检查这个delete语句涉及表的哪些对应统计信息,如果统计信息中modification_counter都是0时或modification_counter/rows没有超过20%,表示统计信息没有过期,就不更新统计信息,这个delete语句结束后5万行删除了,这个5万删除后会同步更新sys.dm_db_stats_properties中的数据字典内容。这样这个delete语句删除5万条语句后,只是更新这个delete语句涉及的统计信息在sys.dm_db_stats_properties中rows和modification_counter的信息,但是不会更新统计信息。等下一次语句运行时如果刚好也是涉及这个表的这些统计信息,就会通过查询modification_counter/rows判断到这个值是50%已经大于20%,这个时候就会触发自动更新这个语句涉及的统计信息。所以说统计信息何时更新,只跟两个条件有关,1是有执行的语句,2是执行的语句涉及的表的对应统计信息在sys.dm_db_stats_properties中modification_counter/rows百分百超过20%。如果一个表的统计信息在sys.dm_db_stats_properties中modification_counter/rows百分百超过20%但是之后没有任何语句会涉及这些统计信息,这些统计信息也不会自动更新。
select sc.name as SchemaName,
o.name as ObjectName,
s.name as StatisticsName,
sp.rows as StatisticsRows,
sp.modification_counter,
sp.last_updated as Statistics_LastUpdated
--CONVERT(DECIMAL(18, 2),sp.modification_counter/sp.rows) as Modify_Percentage
from sys.stats s
inner join sys.objects o on s.object_id=o.object_id
inner join sys.schemas sc on sc.schema_id=o.schema_id
cross apply sys.dm_db_stats_properties(s.object_id,s.stats_id) as sp
where o.name not like '%sys%'
and sc.name not like '%sys%'
and sp.rows>0
and CONVERT(DECIMAL(18, 2),sp.modification_counter/sp.rows) < 0.20
--and DATEDIFF(DAY,sp.last_updated,GETDATE())>=7
--and o.name='LHistory'
--and s.name='_WA_Sys_00000005_665875C9'
order by sp.last_updated desc
–一张同样的表有很多信息,比如一张表有20个字段,总计有10个索引,那么对应的字段和索引都会有统计,这表对应的统计信息可能多达30多个。查询优化器通过使用 AUTO_CREATE_STATISTICS 选项创建统计信息时,统计信息名称以 _WA 开头
2、有没有什么数据字典判断统计信息过期了
没有直接的数据字典,即没有表或视图提供了具体的单个字段名称来显示统计信息是否过期,也就是说没有表或视图有类似statistics_stale这样的字段并且对应值为Yes or No这样明确的判断。只能通过sys.dm_db_stats_properties中modification_counter/rows这两者的比值来判断,其中row表示上次更新统计信息时表或索引视图中的总行数,modification_counter表示自上次更新统计信息以来前导统计信息列(构建直方图的列)的总修改次数。
3、举例子
一张表有10万行,sys.dm_db_stats_properties数据字典中该表对应的统计信息对应的modification_counter都是0,rows都是10万行,执行5次delete,第一次delete 3万行,第二次delete 1行,第三次delete 1万行,第四次delete 1万,第五次delete 1万行,哪几次会触发统计信息自动更新?
第一次:不会
第二次:会
第三次:不会
第四次:不会
第五次:会
4、有没有必要手工更新统计信息
有,因为定时手工更新统计不会对现有系统有任何危害,且在低峰期手工更新统计信息,可以避免语句执行的高峰期间查询优化器去自动更新统计信息,并且统一清理掉sys.dm_db_stats_properties中各表的不同统计信息的历史版本数据。
比如一张表1万行但是有20个字段,总计有10个索引,那么可能超过30个统计信息,字段1只在1年更新一次,字段2只在1周前更新了一次,其他字段可能断断续续隔几小时或几分钟更新一次但是每次更新很少比如只有2次,这样sys.dm_db_stats_properties中该表的不同统计信息可能涉及modification_counter,rows,last_updated三个字段的历史版本会囊括1年的记录,1周前的记录。