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

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

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

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

如下一张业务表,主要看两个“状态”字段,BusinessStatus1 和 BusinessStatus2

create table BusinessTable
(
Id int identity(1,1),
Col2 varchar(50),
Col3 varchar(50),
Col4 varchar(50),
BusinessStatus1 tinyint,
BusinessStatus2 tinyint,
CreateDate Datetime
)
GO --向测试表中写入数据: begin tran
declare @i int
set @i=0
while @i<500000
begin
insert 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+1
end
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. SQL常见优化Sql查询性能的方法有哪些?

    常见优化Sql查询性能的方法有哪些? 1.查询条件减少使用函数,避免全表扫描 2.减少不必要的表连接 3.有些数据操作的业务逻辑可以放到应用层进行实现 4.可以使用with as 5.使用“临时表”暂 ...

  2. 优化SQL 查询性能

    为什么查询会很慢 如果把查询看作是一个任务,那么它由一系列子任务组成,每个子任务都会消耗一定的时间.要优化查询,实际上是要优化其子任务,要么消除其中一些子任务,要么减少子任务的执行次数,要么让子任务运 ...

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

    这段时间AX查询变得非常慢,每天都有很多锁. 最后发现是数据库统计信息需要更新. ----------------------------------------------------------- ...

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

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

  5. Oracle 手动收集统计信息

    收集oracle统计信息 优化器统计范围: 表统计: --行数,块数,行平均长度:all_tables:NUM_ROWS,BLOCKS,AVG_ROW_LEN: 列统计: --列中唯一值的数量(NDV ...

  6. Oracle 判断 并 手动收集 统计信息 脚本

    CREATE OR REPLACE PROCEDURE SchameB.PRC_GATHER_STATS AUTHID CURRENT_USER IS BEGIN SYS.DBMS_STATS.GAT ...

  7. SQL查询性能分析

    http://blog.csdn.net/dba_huangzj/article/details/8300784 SQL查询性能的好坏直接影响到整个数据库的价值,对此,必须郑重对待. SQL Serv ...

  8. Mysql sql查询性能侦查

    Mysql 服务性能优化配置:http://5434718.blog.51cto.com/5424718/1207526[该文章很好] Sql查询性能优化 对Sql进行优化,肯定是该Sql运行未能达到 ...

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

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

随机推荐

  1. 关于PHP中<?php ?>的结束标签是否添加

    在纯PHP代码中“?>”结束标签最好不要添加 原因:容易导致输出多余的空白或者换行,以及由此产生的一些报错. 比如a.php文件中,在?>标签后面多出空格或者换行,而当b.php文件引入a ...

  2. 游戏编程技巧 - Type Object

    Type Object 使用场景 你在制作一款和LOL类似的游戏,里面有许多英雄,因此你想建立一个英雄基类,然后把各种英雄都继承自该基类,这些英雄类都有生命值和攻击力等属性.每次策划想增加一个英雄,你 ...

  3. Ruby 里的 %Q, %q, %W, %w, %x, %r, %s, %i (译)转

    原文地址  转自 %Q 用于替代双引号的字符串. 当你需要在字符串里放入很多引号时候, 可以直接用下面方法而不需要在引号前逐个添加反斜杠 (\") >> %Q(Joe said: ...

  4. mac远程桌面连接windows 8.1 update,提示: 远程桌面连接无法验证您希望连接的计算机的身份

    在网上找到解决方案: SolutionEnable RDP security layer in Group Policy on the machine: Verify that the firewal ...

  5. 我的ORM汇总

    MyOql是我写的ORM,目前仅支持 MSSql2005+ ,从2009年到今天,已使用过不少项目,之后会写 其它关系数据库的解析器: MySql,Sqlite,Oracle 等. 代码地址(最新版) ...

  6. 剑指Offer面试题:2.二维数组中的查找

    一.题目:二维数组中的查找 题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. ...

  7. 大话JS面向对象之开篇万物皆对象------(ATM取款机引发的深思)

    一,总体概要 OO(面向对象)概念的提出是软件开发工程发展的一次革命,多年来我们借助它使得很多大型应用程序得以顺利实现.如果您还没有掌握并使用OO进行程序设计和开发,那么您无疑还停留在软件开发的石器时 ...

  8. 分享一个css3写的气泡对话框,适合于即时通讯和留言本的动态内容

    效果预览: css code .message_content{width:100%;margin-top:10px;clear:both;float:left;} .face{float:left; ...

  9. Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(3)

    Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(3) 到上一篇为止,拾取一个VBO里的单个图元的问题已经彻底解决了.那么来看下一个问题:一个场景里可能会有多个VBO,此时每个 ...

  10. 《Entity Framework 6 Recipes》中文翻译系列 (22) -----第五章 加载实体和导航属性之延迟加载

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第五章 加载实体和导航属性 实体框架提供了非常棒的建模环境,它允许开发人员可视化地使 ...