本质原因在于:SQL Server 统计信息只包含复合索引的第一个列的信息,而不包含复合索引数据组合的信息

来源于工作中的一个实际问题,

这里是组合列数据不均匀导致查询无法预估数据行数,从而导致无法选择合理的执行计划导致性能低下的情况

我这里把问题简单化,主要是为了说明问题

如下一张业务表,主要看两个“状态”字段,BusinessStatus1 和 BusinessStatus2create table BusinessTable
(Id int identity(1,1),Col2 varchar(50),Col3 varchar(50),Col4 varchar(50),BusinessStatus1 tinyint,BusinessStatus2 tinyint,CreateDate Datetime
)
GO--向测试表中写入数据:begin trandeclare @i intset @i=0while @i<500000begininsert into BusinessTable values (NEWID(),NEWID(),NEWID(),1,10,GETDATE()-RAND()*1000)insert into BusinessTable values (NEWID(),NEWID(),NEWID(),1,20,GETDATE()-RAND()*1000)insert into BusinessTable values (NEWID(),NEWID(),NEWID(),1,30,GETDATE()-RAND()*1000)insert into BusinessTable values (NEWID(),NEWID(),NEWID(),2,20,GETDATE()-RAND()*1000)insert into BusinessTable values (NEWID(),NEWID(),NEWID(),2,30,GETDATE()-RAND()*1000)insert into BusinessTable values (NEWID(),NEWID(),NEWID(),2,40,GETDATE()-RAND()*1000)insert into BusinessTable values (NEWID(),NEWID(),NEWID(),3,30,GETDATE()-RAND()*1000)insert into BusinessTable values (NEWID(),NEWID(),NEWID(),3,40,GETDATE()-RAND()*1000)insert into BusinessTable values (NEWID(),NEWID(),NEWID(),3,50,GETDATE()-RAND()*1000)set @i=@i+1end
commit--插入一条特殊数据,也就是实际业务场景中:
insert into BusinessTable values (NEWID(),NEWID(),NEWID(),3,10,GETDATE()-RAND()*1000)

--测试数据的特点是:--BusinessStatus1 的分布位:1,2,3,
--BusinessStatus2 的分布位:10,20,30,40,50--目前数据的对应关系,--但是注意插入的一条特殊数据:
--BusinessStatus1 和 BusinessStatus2 的组合为:BusinessStatus1=3 and BusinessStatus2=10,在451W条数据中是唯一的一个组合--创建如下索引:
Create Clustered index idx_createDate on BusinessTable(CreateDate)Create Index idx_status on BusinessTable(BusinessStatus1,BusinessStatus2)

进行如下查询,就是查询那条所谓的特殊数据

select *
from BusinessTable
where BusinessStatus1=3 and BusinessStatus2=10

发现执行计划如下:走的是全表扫描,IO代价也不小,

这种情况下,明明只有一条数据,却要走全表扫描

(实际业务中类似数据也不仅只有一条这么巧,但是在千万级的表中,符合类似条件的数据很少,

打个比方好理解一点,就像订单表一样,订单是退订状态,且尚未退款,这种数据的分布是少之又少吧

只是举例,不要较真)

上面查询的IO信息

再通过强制索引提示的情况下,发现同样的查询,IO有一个非常大的下降

分析上述sql为什么不走索引?因为毕竟符合条件的数据只有一条,走全表扫描代价也过于大了,尤其是实际情况中,业务表更大,逻辑也没有这么直白

这个还要从索引统计信息说起,在符合索引中,索引统计信息只是统计前导列的,对于组合列的分布,sqlserver是无法预估到的,这一点可以通过第一个查询的执行计划发现

sqlserver只是能够预估到 BusinessStatus1 =3 的情况下的数据分布,但是无法预估到 BusinessStatus1=3 and BusinessStatus2=10这个组合情况下的数据分布情况

当然通过统计信息也可以看到,统计信息只记录了BusinessStatus1的列的数据分布情况,但是实际执行的过程中,无法预估BusinessStatus1=3 and BusinessStatus2=10的准确分布

找到了问题的原因,就容易解决了,既然sqlserver无法预估到BusinessStatus1=3 and BusinessStatus2=10这个组合条件的数据分布请,

那么就创建一个过滤统计信息,让sqlserver准确地知道这个条件下数据的分布请,就容易做出相对准确的执行计划了

通过如下语句,创建一个该条件的统计信息

create statistics BusinessTableFilterStatistics
on BusinessTable(BusinessStatus1,BusinessStatus2)
where BusinessStatus1=3 and BusinessStatus2=10--创建完统计信息之后注意要做个更新
UPDATE STATISTICS BusinessTable BusinessTableFilterStatistics with fullscan

创建完统计信息之后,发现表上会增加一个刚刚创建的统计信息

现在再来看这个查询的执行计划情况,发现其按照预期的走了索引

同时观察起IO情况,也有一个大幅度的下降

总结:

以上通过手动创建统计信息,来促使sqlserver在生成执行计划的时候,准确地知道数据的分布情况,做出较为优化的执行计划,在某些特殊的情况下,可以作为优化的一个考虑方向

后记:

或许有人认为这个问题该归结于parameter sniff的问题,其实这个问题跟parameter sniff还不太一样(当然也有一点像)

通常情况下,所说的parameter sniff问题是单列数据分布不均匀的情况下,因为执行计划重用导致性能地下的一个现象,重点是执行计划的不合理重用

这里的问题在于,由于统计信息的数据计算方式,sqlserver 压根无法预估到符合条件数据的准确分布,从而无法做出合理的执行计划的情况

当然这种情况也比较特殊,在强制索引提示以外,可以通过手动创建统计信息来达到优化的目的

通过手动创建统计信息优化sql查询性能案例相关推荐

  1. execution 排除_使用SQL Server 2016 Live Execution统计信息对SQL查询性能进行故障排除

    execution 排除 SQL Server Management Studio a graphical interactive that allows you to interact with t ...

  2. 常见优化Sql查询性能的方法收集

    1.查询条件减少使用函数,避免全表扫描 2.减少不必要的表连接 3.有些数据操作的业务逻辑可以放到应用层进行实现 4.可以使用with as 5.使用"临时表"暂存中间结果 6.不 ...

  3. 性能优化——统计信息——SQLServer自动更新和自动创建统计信息选项

    原文: 性能优化--统计信息--SQLServer自动更新和自动创建统计信息选项 原文译自:http://www.mssqltips.com/sqlservertip/2766/sql-server- ...

  4. t–sql pl–sql_不正确SQL Server统计信息– SQL查询性能的杀手–基本知识

    t–sql pl–sql 什么是SQL Server统计信息? (What are SQL Server statistics?) SQL Server statistics are a collec ...

  5. oracle 最大值及其_学习笔记:Oracle优化 SQL查询最大值 最小值时的优化方法案例...

    天萃荷净 select max(id),min(id) from table优化,分享开发DBA需求,在SQL语句查询最大值.最小值数据时的优化方式案例 1.查看数据库版本 SQL> selec ...

  6. mysql统计数量函数方法_mySql关于统计数量的SQL查询操作

    mySql关于统计数量的SQL查询操作,状态,订单,语句,函数,数量 mySql关于统计数量的SQL查询操作 易采站长站,站长之家为您整理了mySql关于统计数量的SQL查询操作的相关内容. 我就废话 ...

  7. 预编译sql查询语句_频繁查询重新编译– SQL查询性能杀手–检测

    预编译sql查询语句 previous part of this article, we presented query recompilation basics. We explained when ...

  8. t–sql pl–sql_糟糕SQL查询设计– SQL查询性能的杀手–基本知识

    t–sql pl–sql Depending on the performance problem cause, fixing poor SQL query design can be quick o ...

  9. SQL Server-聚焦sp_executesql执行动态SQL查询性能真的比exec好?

    前言 之前我们已经讨论过动态SQL查询呢?这里为何再来探讨一番呢?因为其中还是存在一定问题,如标题所言,很多面试题也好或者有些博客也好都在说在执行动态SQL查询时sp_executesql的性能比ex ...

最新文章

  1. ResNets首次反超有监督学习!DeepMind用自监督实现逆袭,无需标注
  2. bzoj 2109: [Noi2010]Plane 航空管制
  3. gbq6的文件能转换成gbq5_PPT文件转换成PDF怎么转?这些方法能实现快速转换
  4. 在shell脚本中调用sqlplus
  5. FIR_01 基于FPGA的FIR滤波器 (FDATOOL ISE ) 第一篇:初步认识和应用
  6. mysql 首次连接慢_mybatis+mysql,第一次数据库连接很慢怎么回事?
  7. python中安装包出现Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None))…………
  8. PHP入门教程-hello world
  9. [数字图像处理]模糊算法用于图像增强
  10. Idea、pycharm、Phpstorm鼠标滑动设置字体大小方法
  11. Android-Glide清除缓存图片
  12. android webview 加载过程,实战:七步完成Android Webview图片加载
  13. 经济的寒冬,数据的春天
  14. 计算机丢失wlanapi all,如何解决wlanapi.dll丢失的问题
  15. iOS开发笔记——PDF的显示和浏览
  16. 从来不敷面膜的人_女人一旦过了30岁,敷面膜要记住“4不要”,否则还不如不敷!...
  17. 使用HTML版制作个人简历制作,非常好看的模板!!!
  18. 微信储存卡已拔出,如何解决
  19. 济南市支取公积金办理流程
  20. Win10软件界面乱码,显示日文文字

热门文章

  1. 【Spring实战】—— 3 使用facotry-method创建单例Bean总结
  2. 关于滴滴智能调度的分析和思考
  3. mosquitto---config.mk
  4. 本地实现ES6转ES5代码——gulpfile配置文件
  5. iOS开发之UIWebView
  6. 代理工具Charles使用
  7. nagios监控haproxy(借助脚本)
  8. 求助请IT外包商如何帮用户管好网络?
  9. Spring Hibernate Mybatis配置详解
  10. 开源中国 Maven 库