相关列的基数计算

在今天的文章里我想谈下基数计算里的一个特定问题:在查询谓语里相关列的基数计算。首先我们看下在SQL Server 7.0起的操作方法,最后我们详细看下SQL Server 2014里,在查询期间,处理相关列基数计算的全新实现方式。

什么是相关列(Correlated Columns)

在我们进入问题细节前,我们必须要澄清什么是相关列。当我们看SQL Server使用的查询优化器时,查询优化器是基于4个核心假设:

  • 独立性(Independence)
  • 一致性(Uniformity)
  • 密封度(Containment)
  • 夹杂物(Inclusion)

我不想细谈每个假设,因为它们在其它的白皮书里解释得非常清楚。在文章末尾的小结部分你会找到白皮书的链接。今天我们要聚焦的是第1个假设——独立性(Independence)。独立性意味这在查询谓语(WHERE子句)里用到的列是独立的,当各自查询时,会返回不同的记录。它们彼此间互不影响。遗憾的是这个假设并不都正确。我们来看一个具体的例子,这里违反了假设。假设下列2个查询:

1 SELECT * FROM Sales.SalesOrderHeader
2 WHERE SalesOrderID > 74000 AND SalesOrderID < 75000
3 GO
4
5 SELECT * FROM Sales.SalesOrderHeader
6 WHERE OrderDate >= ‘20080626‘ AND OrderDate <= ‘20080724‘
7 GO

第1个查询返回999条记录,第2个查询返回1125条记录。但符合这2个查询条件的记录是912条,这就是说这2列之间是有关联的。但是查询优化器并没有意识到这点。

SQL Server 7.0-2012的基数计算

现在我们来看下SQL Server 7.0-2012是如何处理这个相关列的。在具体的执行计划里,第1个查询里,查询优化器估计行数是999.936,第2个查询里,查询优化器估计行数是1125。

用那些信息就与基数计算我们的表(这里表总记录数是31465),我们可以计算出查询谓语所谓的选择度(Selectivity)。选择度是0到1的数字。选择度越小,从查询返回的记录越少(0表示0%的记录返回,1表示100%的记录返回)。我们可以通过估计行数除以表存储的总记录数来计算查询谓语的选择度。因此第1个查询谓语的选择度是0.03177931034482758620689655172414(999/31465),第2个查询谓语的选择度是0.03575401239472429683775623708883(1125/31465)。现在我们来看下如果我们使用AND运算符组合2个查询谓语会发生什么:

1 SELECT * FROM Sales.SalesOrderHeader
2 WHERE SalesOrderID > 74000 AND SalesOrderID < 75000
3 AND OrderDate >= ‘20080626‘ AND OrderDate <= ‘20080724‘
4 GO

当你查看执行计划时,查询优化器在聚集索引查找(聚集的)(Clustered Index Seek (Clustered)  )运算符上的估计行数是35.7517。

实际执行返回是如刚才提到的912行。这个差异太大了。查询优化器只是把2个查询谓语的选择度值相乘得出最后的估计行数。SQL Server假设2个查询谓语返回不同的行——假设这2列是彼此独立的:

0.03177931034482758620689655172414 * 0.03575401239472429683775623708883 * 31465 = 35.7517241379310344827586206896586

当然,事实是完全不一样的,因为在2个查询谓语间有巨大的关联。因此你会看到估计行数和行数之间有绝大的差异。在查询里使用更多的AND组合各个查询谓语,差异就会更大。当最后估计将至1行时,查询优化器总会估计至少1行——从不估计0行。

SQL Server 2014的基数计算

你可能已经听说了,SQL Server 2014包含了一个新的基数计算。一旦你的数据库是在120的兼容模式,新的基数计算就会用到。注意,当你从老版本的SQL Server还原或附加数据库时——这里的兼容性会变成老的!如果你想步改变兼容模式使用新的基数计算,你也可以使用新的2312跟踪标记。现在让我们对查询启用2312跟踪标记来让刚才的2个查询谓语使用新的基数计算。

1 SELECT * FROM Sales.SalesOrderHeader
2 WHERE SalesOrderID > 74000 AND SalesOrderID < 75000
3 AND OrderDate >= ‘20080626‘ AND OrderDate <= ‘20080724‘
4 OPTION (RECOMPILE, QUERYTRACEON  2312)
5 GO

当你查看执行计划时,你会看到基数计算已经变了。

现在新的基数计算估计行数是188898.比刚才的老的基数计算的35.75行大很多。但到查询实际返回的912行还是有个大的缺口。不过现在新的基数估计用的是什么公式呢?新的基数计算使用所谓的指数退避算法(Exponential Back-off algorithm)。查询优化器取走这4个查询谓语,根据它们的选择度排序。所有的选择度再次相互相乘,但这里不同的是每个子过程值通过更大的平方根来软化。我们来看下公式来来理解这个行为:

c0 * (c1 ^ 1/2) * (c2 ^1/4) * (c3 ^ 1/8)

我们来看下具体的例子,通过下列计算就可以获得最终的基数:

0.03177931034482758620689655172414 * SQRT(0.03575401239472429683775623708883) * 31465 = 189.075212620762

比起188.898的估计行数我们的计算还有小差异,因为在SQL Server提供给执行计划里的估计行数是999.936行。使用指数退避算法,查询优化器可以确保做出更好的估计收紧估计行数和实际行数的洞,如果接受的查询参数之间有关联的话。

小结

在这篇文章里我们谈了关系数据库里基数计算期间的特定问题:作为查询谓语使用的关联列如何使用基数估计。在SQL Server 2014之前,查询优化器使用不同选择值相乘,非常平稳的方法。这会导致巨大的低估,如果执行计划里前一个运算符(例如Sort或Hash运算符)基于这些估计,它会引起麻烦。

SQL Server 2014新的基数计算对此特定问题使用增强的机制:在基数计算期间使用指数退避公式,生成更好的估计。但和运行时的实际行数还是有差异。如果你想了解更多SQL Server 2014新的基数计算,我建议看下Joe Sack写的白皮书“使用SQL Server 2014参数计算优化你的查询计划”。

感谢关注!

时间: 2024-12-24 05:17:09

相关列的基数计算的相关文章

第16/24周 SQL Server 2014中的基数计算

大家好,欢迎回到性能调优培训.上个星期我们讨论在SQL Server里基数计算过程里的一些问题.今天我们继续详细谈下,SQL Server 2014里引入的新基数计算. 新基数计算 SQL Server 2014里一个增强是新的基数计算.上个星期你已经学到老基数计算有些限制,会生成错误的估计,这会导致不好的执行计划表现.截至SQL Server 2012,你一直在使用自SQL Server 7.0引入的基数计算. 当然,几年来也有很多问题被修正,但默认它们都没启用的——你需要启用SQL Serv

萌新笔记——Cardinality Estimation算法学习(一)(了解基数计算的基本概念及回顾求字符串中不重复元素的个数的问题)

最近在菜鸟教程上自学redis.看到Redis HyperLogLog的时候,对"基数"以及其它一些没接触过(或者是忘了)的东西产生了好奇. 于是就去搜了"HyperLogLog",从而引出了Cardinality Estimation算法,以及学习它时参考的一些文章: http://blog.codinglabs.org/articles/algorithms-for-cardinality-estimation-part-i.html 从文章上看来,基数是指一个

mssql sqlserver 表增加列后,视图不会自动更新相关列的两种解决方法分享

摘要: 今天对物理数据表,进行增加列操作后,程序一直显示无法找到相应列,通过仔细比对发现,视图中无相应列更新,下文将具体的解决方法分享如下: 例: create view vw_test as select * from tableName go ---当我们在表tableName中新增列之,再次查询vw_test ---依然未发现相应列的存在. ----解决方法1:删除原视图,重新创建视图 drop view vw_test go create view vw_test as select *

根据外键名找到主表和关联表的相关列

select fk.name fkname ,ftable.name ftablename ,cn.name fkcol ,rtable.name ftablename ,cn1.name rkcolfrom sysforeignkeys join sysobjects fk on sysforeignkeys.constid = fk.id join sysobjects ftable on sysforeignkeys.fkeyid = ftable.id join sysobjects r

使用awk对某一列进行求和计算

cat file.log file_numb: 3168; total_size: 105.801 GB;avg_size: 34.1983 MBfile_numb: 3154; total_size: 87.16 GB;avg_size: 28.298 MBfile_numb: 348; total_size: 140.047 GB;avg_size: 412.091 MBfile_numb: 306; total_size: 184.478 GB;avg_size: 617.339 MBfi

Optimizer统计信息管理介绍

1.    前言 在我们的日常维护中受理一些一直以来运行得很好的系统,突然有一天用户反馈没有做任何操作,系统的某个功能模块或者是某个报表以前只需要几秒,但现在需要几分钟或更长的时间都没有返回结果.在这样的情况下,我们通常会分析SQL语句,会发现这个SQL的执行计划已经发生改变,在硬件环境未发生变化的情况下,执行计划发生变化多数原因是由于表的统计信息发生了变化,原本使用的某个索引突然间没有被使用,或者使用了较差的索引,这都是由于统计信息不准备确引起的,因此我们有必要了解统计信息的管理和维护,更好的

在计算列中创建索引提高性能

前言:在理解计算列上的索引之前,先了解计算列的基本知识.计算列由可以使用同一表中的其他列的表达式计算得来.表达式可以是非计算列的列名.常量.函数,也可以是用一个或多个运算符连接的上述元素的任意组合.表达式不能为子查询.默认情况下,计算列是一个虚拟的列,并且可以在调用时重新计算,直到在CREATE TABLE或者ALTER TABLE 命令中使用PERSISTED.如果列定义成PERSISTED,会存放计算值,并存放在原始列上更新后的汇总值,不能对计算列进行INSERT.UPDATE. 准备工作:

SQL Server-聚焦计算列或计算列持久化查询性能(二十二)

前言 上一节我们详细讲解了计算列以及计算列持久化的问题,本节我们依然如前面讲解来看看二者查询性能问题,简短的内容,深入的理解,Always to review the basics. 持久化计算列比非持久化计算列性能要好 我们开始创建两个一样的表并都插入100条数据来进行比较,对于计算列我们重新进行创建计算列和非计算列持久化. CREATE TABLE [dbo].[ComputeColumnCompare] (ID INT, FirstName VARCHAR(100), LastName C

SSAS计算列如果是中文名称时,必须要在名字外加中括号

在SSAS中建计算列的时候,如果你给计算列起的是中文名字,一定记住要在名字外加中括号,比如下面这个例子中我们建了一个叫 客服流失数 的计算列 下面图中没有在计算列名称上加中括号这是错误的,因为使用中文名称必须要在列名上加中括号(试了下英文名是可以不加的),否则在处理好的CUBE中你无法看到和使用这个计算列 下面在计算列名称外加上中括号后,就可以在处理好的CUBE中找到和使用这个计算列了