由SQL Server 2005收集的统计信息
SQL Server 2005在表级别维护以下信息。这些信息并不属于统计信息对象,而是SQL Server 2005在某些情况下用来进行查询成本估算的。
? 表或索引的行数(sys.sysindexes表的rows列)
? 表或索引占用的页面数(sys.sysindexes表的dpages列)
SQL Server 2005收集关于表中列的下述统计信息,并存储在一个统计信息对象中(statblob):
? 统计信息收集的时间。
? 用于生成直方图和密度信息的行数(将在下面讲解)。
? 平均键的长度。
? 单列直方图,包含了步数。
? 字符串摘要(如果某一列含有字符串信息)。如果一个统计信息对象包含了字符串摘要,那么DBCC SHOW_STATISTICS输出就会包含“String Index”列,并且该列的值为YES。
一个直方图是给定列的最多200个值的集合。给定列的所有值或抽样值会被排序;排序后的序列最多被划分为199个间隔,以便获得最有效的统计信息。间隔的大小通常是不等的。下面的值或足以派生这些值的信息被存储在直方图的每一步中。
SQL Server 2005只能为单列,或者多列统计信息对象中的第一列创建直方图。
SQL Server 2005通过三步从已排序的一组列值中创建直方图:
? 初始化直方图:在第一步,依次处理以排序集中第一个值作为起点的每个值,最多收集200个RANGE_HI_KEY, EQ_ROWS, RANGE_ROWS以及DISTINCT_RANGE_ROWS(在这一步中,RANGE_ROWS和DISTINCT_RANGE_ROWS始终为零)。当所有输入都被用尽,或者已经找到了200个值,第一步就结束了。
? 使用桶合并进行扫描:按照排序方式处理统计信息中首列的其余列值;每个值要么被添加到最后一个区间,要么被添加到一个新区间(由于输入的值已经排序,因此允许这样做)。如果创建了一个新区间,现有相邻的一对区间将被合并到一个区间。选取相邻的一对区间是为了将信息损失的程度降到最低。整个过程当所有区间合并完成后步数依然是200。该方法是基于maxdiff直方图的一种变形方法。
? 合并直方图:在第三步,如果信息损失的数量不十分明显,就有更多的区间被合并。因此,即使列中唯一值的数目超过了200,那么直方图的步数也可能少于200。
如果通过抽样来生成直方图,那么 RANGE_ROWS、EQ_ROWS、DISTINCT_RANGE_ROWS 和 AVG_RANGE_ROWS的值将为估计值,因此它们不必都是整数。
密度是给定的一列或组合列上关于重复项数目的信息,其计算公式为1/(唯一值数目)。当在一个等值判断表达式中使用了某一列时,就会使用从直方图导出的密度信息来估算满足条件的行数。还可以使用直方图来估算非等值选择判断、连接以及其它操作的选择性。
除了包含用于显示统计信息收集时间的时间戳、表中的行数、为生成直方图而抽样的行数、密度信息和平均的键长度、直方图本身以外,在单列统计信息中还包含了统计信息列集中组成前缀的每一组列的All density值。这些信息显示在DBCC SHOW_STATISTICS输出的第二个结果集中。All density 1/(前缀列集中唯一值得数目)。下一部分将给出相关的一个示例。
注意:dbcc show_statistics返回结果的首行中包含的密度值是所有抽样值的密度而不是RANGE_HI_KEY的密度。RANGE_HI_KEY通常是数据分布中出现频率较高的值。因此该命令所显示的密度值为那些出现频率不高的列值提供了潜在的有价值的信息。
在组合列上创建的多列统计信息包括:统计信息定义中首列的直方图,首列的密度值,以及每个前缀组合列(单独包括首列)的密度值。每组多列统计信息(一个直方图加上两个或多个密度值)都存储在一个statblob中,再加上上次更新统计信息的时间戳、用于生成统计信息的抽样行数、直方图的步数和平均键长度。只包含首列的字符串摘要,如果首列中含有字符型数据。
使用sp_helpindex和sp_helpstats来显示特定表上所有可用的统计信息。sp_helpindex列出表上所有的索引,而sp_helpstats列出表上所有的统计信息。每个索引都含有索引列的统计信息。使用CREATE STATISTICS命令创建的统计信息与在同一列上使用CREATE INDEX命令创建的统计信息是等价的。唯一不同的是CREATE STATISTICS命令默认使用抽样而CREATE INDEX则使用fullscan收集统计信息,因为创建索引时无论如何都必须处理所有的行。
创建和显示统计信息:示例
下面的例子说明如何自动和手动的创建统计信息,以及如何列表和显示关于统计信息的相关资料。示例中给出了某些命令但不是所有的命令的运行结果;如果某个SQL Server 2005命令的输出结果对于阐述统计信息的行为很有用,那么该结果就显示出来。您可以自己运行这个示例来查看完整的输出。
USE tempdb
GO
-- 清理以前运行脚本生成的对象
IF object_id(N'Person.Contact','U') IS NOT NULL
DROP TABLE Person.Contact
GO
IF EXISTS (SELECT * FROM sys.schemas WHERE name = N'Person')
DROP SCHEMA Person
GO
-- 创建示例的schema和表
CREATE SCHEMA Person
GO
CREATE TABLE Person.Contact(
FirstName nvarchar(60),
LastName nvarchar(60),
Phone nvarchar(15),
Title nvarchar(15)
)
GO
-- 加工表中的行
INSERT INTO Person.Contact
VALUES(N'James',N'Smith',N'425-555-1234',N'Mr')
INSERT INTO Person.Contact
VALUES(N'James',N'Andersen',N'425-555-1111',N'Mr')
INSERT INTO Person.Contact
VALUES(N'James',N'Andersen',N'425-555-3333',N'Mr')
INSERT INTO Person.Contact
VALUES(N'Christine',N'Williams',N'425-555-0000',N'Dr')
INSERT INTO Person.Contact
VALUES(N'Susan',N'Zhang',N'425-555-2222',N'Ms')
GO
-- 显示在Person.Contact表上还不存在统计信息
sp_helpstats N'Person.Contact', 'ALL'
GO
-- 在LastName列上隐式地创建统计信息
SELECT * FROM Person.Contact WHERE LastName = N'Andersen'
GO
-- 显示在LastName列上自动创建的统计信息
sp_helpstats N'Person.Contact', 'ALL'
GO
结果:
-- 创建索引,这将同时创建统计信息
CREATE NONCLUSTERED INDEX Phone on Person.Contact(Phone)
GO
-- 显示创建索引时会同时创建相关的统计信息
sp_helpstats N'Person.Contact', 'ALL'
GO
结果:
-- 在firstname和lastname列上创建统计信息
CREATE STATISTICS FirstLast ON Person.Contact(FirstName,LastName)
GO
-- 显示表上现在有三个统计信息对象
sp_helpstats N'Person.Contact', 'ALL'
GO
结果:
-- 显示LastName列的统计信息
DBCC SHOW_STATISTICS (N'Person.Contact', LastName)
GO
结果:
统计对象的标题信息:
-- If you take the name of the statistics object displayed by
-- the command above and subsitute it in as the second argument of
-- DBCC SHOW_STATISTICS you can form. a command like the following one
--(the exact name of the automatically created statistics object
-- will typically be different for you).
DBCC SHOW_STATISTICS (N'Person.Contact', _WA_Sys_00000002_2D7CBDC4)
-- Executing the above command illustrates that you can show statistics by
-- column name or statistics object name.
GO
-- The following displays multi-column statistics. Notice the two
-- different density groups for the second rowset in the output.
DBCC SHOW_STATISTICS (N'Person.Contact', FirstLast)
结果(仅第二个结果集)
列集的前缀以及相关的密度和长度:
如果希望看到一张大型表的完整生成的直方图,运行下面的命令:
USE AdventureWorks
-- 清理以前运行脚本产生的对象
IF EXISTS (SELECT * FROM sys.stats
WHERE object_id = object_id('Sales.SalesOrderHeader')
AND name = 'TotalDue')
DROP STATISTICS Sales.SalesOrderHeader.TotalDue
GO
CREATE STATISTICS TotalDue ON Sales.SalesOrderHeader(TotalDue)
GO
DBCC SHOW_STATISTICS(N'Sales.SalesOrderHeader', TotalDue)
使用SQL Server 2005创建统计信息
您可以在SQL Server 2005中通过以下描述的几种不同的方式创建统计信息。
? 当AUTO_CREATE_STATISTICS开启时(这是默认设置),查询优化器对SELECT, INSERT, UPDATE和DELETE语句进行优化时自动创建单列的统计信息。
? SQL Server 2005中有两个基本语句可以显式地生成上面描述的统计信息:CREATE INDEX首先生成索引,然后再为构成索引键的那些复合列(但并不包含其它列)生成一组统计信息。CREATE STATISTICS为指定的一列或复合列生成统计信息。
? 此来还有几种其它方法可以创建统计信息或索引。但从根本上来说都是使用上面的两个命令。使用sp_createstats为当前数据库中所有用户表的所有符合条件的列创建统计信息(除了XML列)。如果列上几经具有了统计信息对象,则不再为该列创建新的统计信息对象。
? 使用dbcc dbreindex重建指定数据库中某张表上的一个或多个索引。
? 在“Management Studio”中展开Table对象下面的文件夹,右键单击Statistics文件夹,选择New Statistics。
? 使用数据库优化向导(DTA)创建索引。
这里是在 AdventureWorks.Person.Contact表上使用CREATE STATISTICS命令的示例:
CREATE STATISTICS FirstLast2 ON Person.Contact(FirstName,LastName)
WITH SAMPLE 50 PERCENT
通常,使用默认抽样比率的统计信息足以生成一个好的执行计划。但是,也存在增加抽样规模使统计信息更利于查询优化的情况,例如某个列上抽样值并非随机的。如果数据是排序的或者聚簇的,那么就可能产生非随机抽样。创建索引或者将数据加载到已经排序或聚簇的堆中都可能导致数据排序或者聚簇。最常用的大规模抽样就是fullscan,因为它能够带来最准确的统计信息。使用更大规模抽样的统计信息其代价就是创建统计信息所耗费的时间。
上面的例子创建了一个2列的统计信息对象。在这个例子中,由于表太小了,因此忽略了SAMPLE 50 PERCENT而是执行一次完全扫描。抽样主要是为了避免大量的数据扫描,只有包含了1,024或更多页面(8 MB)的表或索引才会进行抽样。
在SQL Server 2005中,当创建索引时会自动创建统计信息。当编译查询时,SQL Server也会自动创建单列统计信息。优化器为那些需要估算密度或数据分布的列自动创建统计信息。以下是该规则的几个例外:当(1)数据库是只读的(2) 太多的显著的查询编译正在进行中(3) 列的数据类型不支持自动创建统计信息,此时SQL Server 不会自动创建统计信息。
通过执行下面的语句可以在数据库级别禁用自动创建统计信息功能
ALTER DATABASE dbname SET AUTO_CREATE_STATISTICS OFF
同样地,执行下面的语句在数据库级别启用自动创建统计信息功能
ALTER DATABASE dbname SET AUTO_CREATE_STATISTICS ON
建议您将该选项设置为ON。只有当您需要解决某些性能问题,例如需要为某些表指定不同于默认值的抽样比率时,才禁用该选项。
默认情况下,当执行CREATE STATISTICS命令或者自动创建统计信息时,是通过对数据集抽样的方式来创建统计信息。CREATE INDEX总是扫描整个数据集,因此最初创建的索引统计信息是没有抽样的(相当于fullscan)。CREATE STATISTICS命令允许您设置抽样规模,在WITH子句中要么指定fullscan,要么指定扫描数据或行数的百分比。后者作为近似值来处理。也可以在UPDATE STATISTICS命令中指定WITH RESAMPLE来继承原有的抽样规模。这一点对于那些在某些列有索引(最初通过fullscan统计创建的)而其他一些列上只有统计信息(最初通过SAMPLE统计创建的)的表来说是十分重要的。在UPDATE STATISTICS上使用resample选项将为索引维持fullscan统计,为其余的列维持抽样统计。
对于小型表至少需要抽样8MB的数据。如果一张表开始时很小,您使用默认的抽样比率进行抽样,此后表的大小增长超过了8MB,那么当您使用resample选项更新统计信息时,您得到的同样还是fullscan。如果您希望默认的抽样比率随着表的规模而变化,则应该避免使用resample。
resample抽样比率是在前一次统计信息计算时根据已抽样行数和表中总行数的函数进行计算的。由于真实的抽样比率可能因抽样随机性的特点而变化,因此对于非完全扫描抽样来说,resample只是近似于上一次的抽样比率。如果希望反复一致地进行抽样,在使用UPDATE STATISTICS之前显式地指定相同的抽样比率而不是使用resample。
dbcc show_statistics命令在Rows Sampled heading下面显示抽样规模。自动创建的统计信息,或者自动更新的统计信息(将在下一部分描述)总是进行默认的抽样。默认的抽样比率是一个表规模的慢速增长函数,这样即使是十分大型的表也可以相对快速地收集统计信息。
当创建和更新统计信息时,查询优化其必须选择一种存取路径来收集统计信息。存取路径可以包含堆、聚簇索引,或者非聚簇索引。对于抽样的统计信息,优化器尽量避免那些对统计信息首列进行物理排序的存取路径,这样做有助于提供更随机化的抽样,也因此导致更加精确的统计信息。对于那些没有对统计信息健值作排序的存取路径(如果存在这样的存取路径),则选择其中成本最低的一种,这就是最精确的索引或者堆。对于fullscan统计,由于存取路径的排序顺序对于统计信息的准确性无关紧要,因此成本最低的存取路径将被选中。
SQL Server Profiler可以监视自动的统计信息创建。Auto Stats事件属于Performance跟踪事件组. 当定义跟踪时,还要同时选中Auto Stats的IntegerData,Success和ObjectID列。一旦捕获到AutoStats事件,Integer Data包含了在指定表上创建或者更新的统计信息的数目, Object ID为表的ID,TextData(默认包含在跟踪定义中)包含了创建或者更新统计信息的列名,再加上Updated: 或者Created: 前缀。Success包含了Auto Stats操作成功或者失败的标记。 需要特别指出的是,Success有三个可能的值:
有时您可能还会观察到AutoStats事件但并没有与之相关的统计信息创建或更新操作发生。该事件的产生是由于auto update statistics选项被关闭,或者由于查询引用的表上产生了大量的变更,由于查询结构和外健约束的存在从而导致优化器从查询计划中移除所有对该表的引用。
DROP STATISTICS用于删除统计信息,但是不可以删除作为创建索引的副产品而自动生成的统计信息。这种统计信息只有当删除索引时才会被删除。
在SQL Server 2005中维护统计信息
当您在表中进行了一系列的插入,删除和更新后,统计信息可能无法反映出特定列或索引的真实数据分布情况。如果SQL Server查询优化器需要表中某一列的统计信息,该表在上次创建或更新统计信息后经历了大量的更新活动,那么SQL Server就会通过抽样列值的方式自动更新统计信息。(使用auto update statistics)。统计信息的自动更新是由查询优化器触发的,或者通过执行一个编译好的执行计划而被触发,并且只更新那些在查询中被引用的列的子集。如果AUTO_UPDATE_STATISTCS_ASYNC为OFF,那么在编译查询之前更新统计信息,如果AUTO_UPDATE_STATISTCS_ASYNC为ON,则异步更新统计信息。
当一个查询首次被编译时,如果查询优化器需要一个特定的统计信息对象并且该对象存在,该统计信息对象将被更新如果统计信息对象已经过时。当执行一个查询且该查询的执行计划在缓存中,那么执行计划所依赖的统计信息将被检查已确认是否已经过时。如果是,执行计划将从缓存中移除,统计信息在重新编译查询时被更新。如果执行计划所依赖的统计信息改变了,那么执行计划也将从缓存中移除。
SQL Server 2005根据列更新计数器(colmodctrs)的变化决定是否更新统计信息。
以下情况可以认定一个统计信息对象已经过时:
? 如果在一张普通表上定义统计信息,那么该统计信息将过时,如果:
1. 表的大小从0行增长到>0行。
2. 当收集统计信息时,表中行数为500行或更少,此后统计信息对象中首列的colmodctr的变更超过了500。
3. 当收集统计信息时,表中含有500行以上的记录,此后统计信息对象中首列的colmodctr变更超过了500 + 收集统计信息时表中行数的20%。
? 如果在一张临时表上定义统计信息,那么在前面描述的情况下统计信息也将过时。
表变量根本没有统计信息。
可以在不同的级别上关闭前面描述的auto update statistics特性。
? 在数据库级别,使用下面的语句关闭自动更新统计信息:
ALTER DATABASE dbname SET AUTO_UPDATE_STATISTICS OFF
? 在表级别,使用UPDATE STATISTICS命令或者CREATE STATISTICS命令的NORECOMPUTE选项关闭自动更新统计信息。
? 使用sp_autostats显示和修改表、索引、或者统计信息对象的自动更新设置。
同样地,使用ALTER DATABASE,UPDATE STATISTICS或者sp_autostats可以重新启用自动更新统计信息。
SQL Server 2005在数据库、表、索引或者统计信息对象级别维护自动更新统计信息的设置。当某人使用sp_autostats命令修改了某张表上所有对象的该项设置时,SQL Server通过单独修改表上每个统计信息对象和索引的设置来实现。不存在直接记录整张表自动更新统计信息设置为ON或OFF的元数据。
下表显示结合不同的数据库、表、索引设置的效果:
在数据库级别设置的OFF是无法通过在统计信息对象级别将自动更新统计信息选项设置为ON来改写的。
自动更新统计信息始终通过对表或索引按默认的抽样比率进行抽样来完成。要想显式地设置抽样比率,运行CREATE或者UPDATE STATISTICS。
更新统计信息包含在与创建统计信息相同的SQL Profiler事件中。