续前一篇——有关SQL SERVER分布统计的问题 (http://www.2cto.com/database/201203/123992.html)
查询统计
现在我们来粗略地看下直方图的工作方式,重要的是要了解如何查询表中的统计个数,幸运的是可以使用以下命令来查询:
1: SELECT Schema_name(sys.objects.schema_id) + '.' + Object_Name(sys.stats.object_id) AS Table_Name,
2: sys.columns.name AS Column_Name,
3: sys.stats.Name AS Stats_Name
4: FROM sys.stats
5: INNER JOIN sys.stats_columns
6: ON stats.object_id = stats_columns.object_id
7: AND stats.stats_id = stats_columns.stats_id
8: INNER JOIN sys.columns
9: ON stats_columns.object_id = columns.object_id
10: AND stats_columns.column_id = columns.column_id
11: INNER JOIN sys.objects
12: ON stats.object_id = objects.object_id
13: LEFT OUTER JOIN sys.indexes
14: ON sys.stats.Name = sys.indexes.Name
15: WHERE sys.objects.type = 'U'
16: --AND sys.objects.name = 'Tab1'
17: ORDER BY Table_Name
18: GO
查询结果:
创建统计
在对你的
数据库运行上面的查询时看到的统计对象是自动创建的,当然,你也可以通过运行以下命令来手动创建:
1: CREATE STATISTICS Stats_MyStatOnCol1 ON t1(i) WITH FULLSCAN
上面的这条命令将对表t1的i列上创建一个统计对象,同时使用WITH FULLSCAN参数来扫描整个表,而不是采样部分行,主要是为了得到更为精确的统计直方图。
不过,FULL SCAN的开销也是比较大的,但是要比采样扫描更为精确。
采样扫描
默认,SQL SERVER会根据现有的统计对象是否过期来进行创建或更新统计;当检测到与当前数据不匹配时,SQL SERVER会采样表中的数据进行重建统计,默认的采样频率是根据表的大小进行缓慢增加。
当使用采样创建统计时,SQL SERVER会从IAM链中随机选取一些页面,一旦某个页面选定后,页面中的这些数据就作为采样的数据源,这偶尔对导致一些不正确的数据统计,为此,你可能使用FULLSCAN来重建统计。
注意:有关IAM链的信息可以从这个链接http://www.sqlskills.com/blogs/paul/post/Inside-the-Storage-Engine-IAM-pages-IAM-chains-and-allocation-units.
aspx获取详细的内容,另外,使用下面的信息来检测标记过期统计的方法,当统计的列发生更新时,SQL SERVER会根据实例中设置的“自动更新统计”来保持数据最新,其工作的方法如下:
当表的行数小于6条,并且该表存储在TEMPDB数据库中,每发生6次修改会触发自动更新
当表的行数大于6条,并且小于等于500,每发生500次修改会触发自动更新
当表的行数大于500条,表中的(500+20%)的数据发生变化会触发自动列新
对于表变量来说,并不会触发自动更新
全扫描
在有些表中,经常看到由于某些列发生变化,与这些列相关的统计也会自动更新,但是若发现创建的统计是基于采样的数据,那你可能需要手动运行
UPDATE STATISTICS WITH FULLSCAN来更新统计,如下命令:
1: UPDATE STATISTICS Tab1 Stats_MyStatOnCol1 WITH FULLSCAN
说到这里,你可能会问:有必要关注数据库中统计所占用的空间吗?
答案取决于多种因素,通常不用过多关注统计所占用的空间,但是也不总是这样;如果你遇到一个含有非常长的列的表,或许你应该调查一下服务器是否因花费过多的时间和资源来更新这些统计的必要性。
在维护期间重建索引和更新统计,对于一个
系统中数据量比较大的表来说,可能删除那些未使用的统计,这样有助于提高重建和统计更新的速度,不过并没有一种方法来检查统计对象是否使用,很难找出哪些统计对象未被使用。
测试
下面来通过一个示例来介绍,脚本创建一个示例表Tab1(26列),然后插入1万行:
1: CREATE TABLE Tab1 (ID Int IDENTITY(1,1) PRIMARY KEY,
2: Col1 VarChar(200) DEFAULT NEWID(),
3: Col2 VarChar(200) DEFAULT NEWID(),
4: Col3 VarChar(200) DEFAULT NEWID(),
5: Col4 VarChar(200) DEFAULT NEWID(),
6: Col5 VarChar(200) DEFAULT NEWID(),
7: Col6 VarChar(200) DEFAULT NEWID(),
8: Col7 VarChar(200) DEFAULT NEWID(),
9: Col8 VarChar(200) DEFAULT NEWID(),
10: Col9 VarChar(200) DEFAULT NEWID(),
11: Col10 VarChar(200) DEFAULT NEWID(),
12: Col11 VarChar(200) DEFAULT NEWID(),
13: Col12 VarChar(200) DEFAULT NEWID(),
14: Col13 VarChar(200) DEFAULT NEWID(),
15: Col14 VarChar(200) DEFAULT NEWID(),
16: Col15 VarChar(200) DEFAULT NEWID(),
17: Col16 VarChar(200) DEFAULT NEWID(),
18: Col17 VarChar