在sql server中,我们要看懂执行计划和统计信息,我们可能需要深刻理解一些关键词,例如密度(density)、选择性(selectivity)、谓词(predicate)、基数(cardinality)。前阵子,对密度和选择性的概念模糊了,刚好看了query tuning fundamentals: density, predicates, selectivity, and cardinality这篇文章, 遂结合自己的理解、以及相关案例、分析总结一下这些专业名称。
谓词(predicate)
什么是谓词呢?谓词是取值为 true、false 或 unknown 的表达式。 谓词用于where子句和having子句的搜索条件中,还用于from子句的联接条件以及需要布尔值的其他构造中。官方的解释为:a predicate is an expression that evaluates to true or false 。在where条里面的常见的谓词形式有:
1: like模糊查询。
2: between范围查询
3: is null、is not null判断
4: in – or
5: exist
6: 等值查询
…………………………
我们先通过例子来看看一个谓词(predicates)吧。如下所示, h.salesorderid > 43669 这个范围查询就是一个过滤谓词。如下所示,在实际执行计划中,右键单击“clustered index seek”查看细节。就会看到seek predicates。
use adventureworks2014
go
select h.*
from sales.salesorderheader h
where h.salesorderid > 43669;
sql server中有两种谓词:过滤谓词和连接谓词 ,还有所谓的sarg谓词和非sarg谓词概念。如上所示,上面的谓词就属于过滤谓词,而位于left/inner/right join的on后面的为连接谓词。 另外在sql server中还有隐式谓词(implied predicates)的概念。使用跟踪标记2324可以禁用隐式谓词。 这里对这些概念不做展开介绍。
密度(density)
密度(density)这个指标是用来衡量一个(或一组)列中,有多少唯一值。 它是一个比率值。 实际应用中值越小越好。不过,首先我们要区分dbcc show_statistics输出的头部信息(stat_header)中的这个density指标和density_vector中的density指标。这两者是有所区别的,其实一般我们所说的密度(density)指density_vector中密度,而不是stat_header中的density。
在dbcc show_statistics输出的头部信息(stat_header),这个density指标,官方文档的介绍如下,具体参考dbcc show_statistics (transact-sql)链接:
density:密度计算公式为 1/统计信息对象第一个键列中的所有值(不包括直方图边界值)的非重复值。 查询优化器不使用此 density 值,显示此值的目的是为了与 sql server 2008 之前的版本实现向后兼容
calculated as 1 / distinct values for all values in the first key column of the statistics object, excluding the histogram boundary values.
this density value is not used by the query optimizer and is displayed for backward compatibility with versions before sql server 2008.
但是这里发现头部信息(stat_header)中density的值计算并不像官方文档介绍的那样(具体见上面所述,这也是我很困惑的地方,个人猜测是文档有错误,一直没人更正,毕竟官方文档也不能保证100%的准确性):
stat_header的density的的计算公式为 ~= count(disitnct column_name)/count(*)
0.607627522644 ~= 0.6162394
注意:上面只能是约等于,不是等于关系。后面找了很多资料,发现其实(stat_header)中的这个density指标的计算公式是这样:
density =
(select distinct (column_name)
from table_name
where column_name not in (histogram range_hi_key values))
/ (select count(column_name)
from table_name
where column_name not in (histogram range_hi_key values))
具体到这个例子来说(对于复合索引,这个字段是符合索引第一个字段),如下所示:
select count(distinct customerid)*1.0/count(*)
from sales.salesorderheader
where customerid not in ( 11000, 11019, 11091, 11142, 11185, 11223, 11262,
11300, 11331, 11417, 11439, 11498, 11519, 11566,
11631, 11677, 11711, 11769, 11892, 11935, 12008,
12054, 12127, 12196, 12291, 12321, 12363, 12489,
12559, 12616, 12760, 12880, 12969, 13038, 13096,
13175, 13231, 13270, 13474, 13575, 13608, 13652,
13756, 13823, 13944, 13988, 14096, 14162, 14265,
14341, 14612, 14860, 14943, 15048, 15114, 15177,
15521, 15625, 15687, 15932, 15974, 16237, 16513,
16583, 16641, 16758, 16855, 16959, 17026, 17103,
17181, 17260, 17335, 17551, 17619, 17715, 17788,
17832, 17930, 18047, 18125, 18223, 18294, 18390,
18452, 18620, 18712, 18749, 19031, 19289, 19339,
19420, 19499, 19585, 20051, 20159, 20245, 20576,
20779, 20862, 20960, 21046, 21248, 21470, 21574,
21807, 21916, 22122, 22344, 22826, 23136, 23267,
23578, 23725, 24159, 24257, 24466, 24754, 24887,
25114, 25400, 25555, 25819, 25916, 25995, 26127,
26276, 26564, 26686, 26841, 27197, 27361, 27672,
28050, 28389, 28749, 28919, 29105, 29270, 29448,
29508, 29603, 29669, 29698, 29723, 29795, 29857,
29927, 29990, 30023, 30096, 30117, 30118 )
由于查询优化器不使用此density值,所以在此略过。我们下面来看看密度向量(density_vector)中的密度计算。
密度向量(density_vector)
use adventureworks2014;
go
dbcc show_statistics('sales.salesorderheader', 'ix_salesorderheader_customerid') with density_vector
密度向量中的密度(density):一个比率值,