对于同一句话,SQL SERVER 有不少种方法来完成它。有些方法适合于数据量比较小的时候,有些方法适合于数据量比较大的时候。同一种方法,在数据量不一样的时候,复杂度会有很是大的差异。索引只能帮助SQL SERVER找到符合条件的记录。SQL SERVRE 还须要知道每一种操做所要处理的数据量有多少,从而估算出复杂度,选取一个代价最小的执行计划。说得通俗一点,SQL SERVR要可以知道数据是“长得什么样”的,才能用最快的方法完成指令。怎么可以让SQL SERVER知道数据的分布信息呢?在数据库管理系统里有个经常使用的技术,就是数据的“统计信息”(STATISTICS)。SQL SERVER 是经过它了解数据的分布状况的。算法
咱们能够看一下上次2张表在SalesOrderID这个字段上的统计信息,以便对于直观认识。sql
先手动更新一下:SalesOrderHeader_TEST 这个表的统计信息,在统计信息的维护上面说明为何这里要手机更新:数据库
UPDATE STATISTICS dbo.SalesOrderHeader_TEST GO -- 再运用脚本: DBCC SHOW_STATISTICS(SalesOrderHeader_TEST , SalesOrderHeader_TEST_CL) GO
结果以下图:less
保存的是每一个订单的概要信息,一张订单只会有一条记录,因此SalesOrderID是不会重复的。如今这张表里,应该有31474 条记录,SalesOrderID是一个int型的字段,因此字段长度是4。运用DBCC SHOW_STATISTICS(<table_name> , <index_name>) 命令能够获得统计信息内容。性能
统计信息内容分3部分spa
列名设计 |
说明code |
|
Nameserver |
统计信息名称。这里就是索引的名字: SalesOrderHeader_test_CL对象 |
|
Updated |
上一次更新统计信息的日期和时间。这里是09 6 2013 4:22PM这个时间很是重要,根据它可以判断统计信息是何时更新的,是否是数据量发生变化之后,是否是存在统计信息不能反映当前数据分布特色的问题。 |
|
Rows |
表中的行数。这里是31474行,彻底正确反映了当前表的数据量 |
|
Rows Sampled |
统计信息的抽样行数。这里也是31474,说明上次SQL SERVER更新统计信息的时候,对整个表里全部记录的SalesOrderID字段,都扫了一遍,这样作出来的统计信息通常都都是很精确的。 |
|
Steps |
在统计信息的第3部分,会把数据分红几组。这里是3组 |
|
Density |
第一个列前缀的选择性(不包括 EQ_ROWS) |
|
Average key length |
若是列的平均长度,由于SalesOrderHeader_test_cl索引只有一列,数据类型是int ,因此长度就是4 |
|
String Index |
若是为“是”,则统计信息中包含字符串摘要索引,以支持为like条件估算结果算大小、nvarchar(max)、text 以及 ntext 数据类型的关导列,这里是int ,因此这个是为”no” |
2. 数据字段的选择性
列名 |
说明 |
|
All density |
反映索引列的选择性(select ivity)“选择性”反映数据集里重复的数据量多少,或者反过来讲,值惟一的数据量有多少,若是一个字段的灵气不多有重复,那么它的可选择性就比较高。好比身份证号,是不可重复的。哪怕对整个中国的身份记录作调查,代入一个身份证号码,最多只会有一条记录返回。在这样字段上的过滤条件,可以有效地过滤掉大量数据,返回的结果集会比较小。举一个相反的例子,性别。非男即女,这个字段上的重复性就很高,选择性就很低,一个过滤条件,最多只能过滤掉一半的记录。 SQL SERVER经过计算“选择性”,使得本身可以预测一个过滤条件作完后,大概能有多少记录返回。 Density的定义是:desity = 1/Cardinality of index keys 若是这个值小于0.1,通常讲这个索引的选择性比较高。若是大于0.1,它的选择就不高了。
|
|
Average length |
索引列的平均长度,这里仍是4 |
|
Columns |
索引列的名称, 这里是字段 SalesOrderID |
从这一部分的信息,能够推断出统计所关心的字段的长度,以及它有多少条惟一值。可是这些信息对SQL SERVER预测结果集复杂度还不够。好比我如今要查一个SalesOrderID = 60000的订单,仍是不知道会有多少记录返回。这里须要第3部分信息
3. 直方图(histogram)
列名 |
说明 |
|
Range_hi_key |
直方图里第一组(step)数据的最大值。订单号的最小号码在表里是43659。这里是sql server选择它做为第一个step的最大值。3组数据分别是:.. ~~43659 , 43660 ~~75131 ,75132~~75132 |
|
Range_rows |
每组数据区间行数据,上限值除外。第一组只有一个数:43659,第三组也只有一个数: 75132. 其它数据都在第二个组里,区别里有31471个数。 |
|
Eq_Rows |
表中与直方图每组数据上限值相等的行数目。 这里都是1 |
|
District_range_rows |
走方图里每组数据区间非重复值的数目,上限值除外。因为这个字段没有重复的值,因此这里distinct_reange_rows的值就等于range_rows的值 |
|
Avg_range_rows |
直方图里每组数据区间内重复值的平均数目,上限值除外。 计算公式 = (Range_rows/district_range_Rows for distinct_range_rows>0)这里district_range_Rows的值就等于 Range_rows的值,因此avg_range_rows = 1 |
|
有这么一个直方图,就可以很好地知道表格里的数据分布了。在SalesOrderID这个字段里,最小值是43659,最大75132,在这个区间里有31473个值,且没有重复值,因此要吧推算出表格里的值就是从43658 到 75132结束的每一个int值、 SQ L SERVER 没有必要存储不少step的信息,只要这3个step,就可以彻底表达数据分布。假设查询条件是在43659 – 75132之间的值,那么SQL SERVER知道会返回一行。若是不在这个区别,就不会有行返回。而返回的每一行长度,都是4. 经过这些统计信息,SQL SERVER可以比较好地预测返回的结果集的行数和长度。
注:1. 若是一个统计信息是为一组字段创建的,例如,一个复合索引创建在两个以上的字段上,SQL SERVER维护全部字段的选择性信息,可是只会维护第一个字段的直方图。
2. 当表格比较大的时候,SQL SERVER 在更新统计信息的时候为了下降消耗,只会取表格的一部分数据作抽样(Rows Sampled),这时候统计信息里面的数据都是根据这些抽样数据估算出来的值,可能和真实值会有些差别。
统计信息越细,就越准确,可是维护统计信息要付出的额外开销也会很大,有可能提升统计信息精确度所带来的执行性能的提高,还抵不了维护统计信息成本的增长。SQL SERVER作这样的设计,不是由于其能力有限,而是为了谋求一个对大多数状况都合适的平衡。
刚才看的索引 SalesOrderHeader_test_CL数据分布比较简单。下面来讲一个稍微复杂一点的索引。SalesOrderDetail_TEST_NCL
SalesOrderDetail_TEST这张表造数据的时候,作的比较特别,它的前10%的数据,属于编号43659 – 75132这3万多条订单,然后90%的数据,平均属于43659 – 75132这9张订单。来看一下统计信息是如何表示的。
DBCC SHOW_STATISTICS(SalesOrderDetail_test, SalesOrderDetail_TEST_NCL)
这个统计信息和SalesOrderHeader_test_CL有不少不一样
一、 这里的数据分组(step)/有190个,要详细不少。
二、 在第2部分Density,不但有索引列(SalessOrderID)的选择值,还有SalesOrderID + SalesOrderDetailID 合并起来的选择值。能够看出若是同时使用2个字段进行过滤,其选择性8.242868E-07 会比只使用 SalesOrderID (3.177226E-05) 还要高
三、 走廊图只有SalesOrderID的信息,没有 SalesOrderDetailID 的信息。从直方图的各项值分布状况,能够清楚地看出 SalesOrderHeader_test 这张表的数据分布特色。SQL SERVER可以根据供稿的 SalesOrderID值,推断出是只有几条、几十条记录返回(当SalesOrderID 在43659到75123之间),仍是会有12万条数据返回(当SalesOrderID 在75124到75132之间)。
下面两段代码虽然结构如出一辙,可是由于参数值不一样,SQL SERVER选择了不一样的执行计划,下图,这是由于SQL SERVER知道一个只会返回3行(EstimateRows), 然后一个会返回 121317行,这里SQL SERVER 猜得是彻底正确的。
SET STATISTICS PROFILE ON 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 SET STATISTICS PROFILE OFF
SET STATISTICS PROFILE ON 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 SET STATISTICS PROFILE OFF
在SQL SERVER数据库属性里,有两个默认打开的设置AUTO_CREATE_STATISTICS 和 AUTO_UPDATE_STATISTICS 他们可以让SQL SERVER在须要的时候,自动创建要用到的统计信息,也能在发现统计信息过期的时候,自动去更新它。 何时会建立统计信息呢?主要有3种状况:
一、 在索引建立时,SQL SERVER会自动地在索引所在列上建立统计信息。
因此从某种角度讲,索引的做用是双重的。它本身可以帮助SQL SERVRE快速找到数据。而它上面的统计信息,也可以告诉SQL SERVER 数据的分布状况
二、 管理员也能够经过 CREATE STATISTICS 之类的语句手动建立他认为须要的统计信息
若是打开 AUTO_CREATE_STATISTICS,通常来说不多须要手动建立。
三、 当SQL SERVER想要使用某些列上的统计信息,发现没有的时候, "auto create statistics" 会让 sql server自动建立统计信息
例如: 当语句要在某个(或者某几个)字段上作过滤,或者要拿它(们)和另一张表联接(JOIN), SQL SERVER 要估算最后从这个表会返回多少条记录。这个时间就须要一个统计信息的支持。若是没有,SQL SERVER会自动建立一个。
USE AdventureWorks2008 GO -- 返回指定表中列和索引的统计信息。(索引上的除外) sp_helpstats @objname = 'dbo.SalesOrderHeader_TEST' go -- 结果显示:此对象没有任何统计信息。 SELECT COUNT(*) FROM dbo.SalesOrderHeader_TEST where OrderDate = '2004-06-11 00:00:00.000' go sp_helpstats @objname = 'dbo.SalesOrderHeader_TEST' go -- 显示结果 /* statistics_name statistics_keys _WA_Sys_00000003_7F80E8EA OrderDate */
由上面的例子能够看出,在打开 AUTO_CREATE_STATISTICS 的数据库上,不用担忧 SQL SERVER没有足够的统计信息来选择执行计划。
SQL SERVER不只要创建合适的统计信息,还要及时更新它们,使他们可以反映表里数据的变化。数据的插入、删除、修改均可能会引发统计信息的更新。可是,更新统计信息自己是一件消耗资源的事情,尤为是对比较大的表。若是有一点点小的修改SQL SERVER都要去更新统计信息,可能 SQL SERVER就光忙活这个,来不及作其它事了。 SQL SERVER 仍是要在统计信息的准确度和资源合理消耗之间作一个平衡。在SQL SERVER 2005/08,触发统计信息自动更新的条件是:
一、若是统计信息是定义在变通表格上的,那么当发生下面变化之一后,统计信息就被认为是过期的,下次使用时,会自动触发一个更新动做。
(1)表格从没有数据变成有大于等于1条数据
(2)对于数据量小于500行的表格,当统计信息的第一个字段数据累计变化大于500之后。
(3)对于数据量大于500行的表格,当统计信息的第一个字段数据累计变化量大于500 + (20%)
二、临时表(temp table) 上能够有统计信息。其维护策略基本和普通表格一致。可是表变量(table variable) 上不能建统计信息。
这样的维护策略可以保证花费比较小的代价,确保统计信息基本正确。后面会有安例,反映这个维护策略在数据分布特殊的表上,也有可能形成一些负面的影响。
在SQL SERVER2005之后,数据库属性多了一个‘Auto Update Statistics Asynchronously’。当SQL SERVER发现某个统计信息过期时,它会用老的统计信息继续如今的查询编译,可是会在后台启动一个任何,更新这个统计信息。这样下一次统计信息被使用时,就已是一个更新过的版本。这样作的缺点,是不能保证当前这句查询的执行计划准确性。凡事有利有弊,数据库管理员能够根据实际状况作选择。 固然,的确有一些例外状况。因为数据特殊性,会使得 SQL SERVER 这种 auto update statistics 的算法不能知足确保执行计划确实性的需求。在实际使用中,有时候数据库的性能会忽然之间慢下来。有经验的管理员会安排一次索引重建的任务,经常对性能会有所帮助。一般人们会解释为,由于索引重建消除了数据碎片,于是提升了性能。其实索引重建还作了另一件很重要的工做,它使用full scan 的方式,从新更新了表上的统计信息,使得统计信息很是精确。这对性能帮助做用也会很大。