最近在维护公司项目时,需要加载某页面,总共加载也就4000多条数据,竟然需要35秒钟,要是数据增长到40000条,我估计好几分钟都搞不定。卧槽,要我是用户的话估计受不了,趁闲着没事,就想把它优化一下,走你。

先把查询贴上:

  1. select Pub_AidBasicInformation.AidBasicInfoId,
  2. Pub_AidBasicInformation.UserName,
  3. Pub_AidBasicInformation.District,
  4. Pub_AidBasicInformation.Street,
  5. Pub_AidBasicInformation.Community,
  6. Pub_AidBasicInformation.DisCard,
  7. Pub_Application.CreateOn AS AppCreateOn,
  8. Pub_User.UserName as DepartmentUserName,
  9. Pub_Consult1.ConsultId,
  10. Pub_Consult1.CaseId,
  11. Clinicaltb.Clinical,AidNametb.AidName,
  12. Pub_Application.IsUseTraining,
  13. Pub_Application.ApplicationId,
  14. tab.num
  15. FROM   Pub_Consult1
  16. INNER JOIN Pub_Application ON Pub_Consult1.ApplicationId = Pub_Application.ApplicationId
  17. INNER JOIN Pub_AidBasicInformation ON Pub_Application.AidBasicInfoId = Pub_AidBasicInformation.AidBasicInfoId
  18. INNER JOIN(select ConsultId,dbo.f_GetClinical(ConsultId) as Clinical
  19. from Pub_Consult1) Clinicaltb on Clinicaltb.ConsultId=Pub_Consult1.ConsultId
  20. left join (select distinct ApplicationId, sum(TraniningNumber) as num from dbo.Review_Aid_UseTraining_Record  where  AidReferralId is null  group by  ApplicationId) tab on tab.ApplicationId=Pub_Consult1.ApplicationId
  21. INNER JOIN(select ConsultId,dbo.f_GetAidNamebyConsult1(ConsultId) as AidName  from Pub_Consult1) AidNametb on AidNametb.ConsultId=Pub_Consult1.ConsultId
  22. LEFT OUTER JOIN Pub_User ON Pub_Application.ReviewUserId = Pub_User.UserId
  23. WHERE Pub_Consult1.Directory = 0
  24. order by Pub_Application.CreateOn desc

执行后有图有真相:

 

这么慢,没办法就去看看查询计划是怎么样:

 

这是该sql查询里面执行三个函数时生成查询计划的截图,一看就知道,执行时开销比较大,而且都是花费在聚集索引扫描上,把鼠标放到聚集索引扫描的方块上面,依次看到如下详细计划:

 
 

从这几张图里,可以看到查询I/O开销,运算符开销,估计行数,以及操作的对象和查询条件,这些都为优化查询提供了有利证据。第1,3张图IO开销比较大,第2张图估计行数比较大,再根据其它信息,首先想到的应该是去建立索引,不行的话再去改查询。

先看看数据库引擎优化顾问能给我们提供什么优化信息,有时候它能够帮我们提供有效的信息,比如创建统计,索引,分区什么的。

先打开SQL Server Profiler 把刚刚执行的查询另存为跟踪(.trc)文件,再打开数据库引擎优化顾问,做如下图操作

 

最后生成的建议报告如下:

 

在这里可以单击查看一些建议,分区,创建索引,根据提示创建了如下索引:

  1. CREATE NONCLUSTERED INDEX index1 ON [dbo].[Pub_AidBasicInformation]
  2. (
  3. [AidBasicInfoId] ASC
  4. )
  5. CREATE NONCLUSTERED INDEX index1 ON [dbo].[Pub_Application]
  6. (
  7. [ApplicationId] ASC,[ReviewUserId] ASC,[AidBasicInfoId] ASC,[CreateOn] ASC
  8. )
  9. CREATE NONCLUSTERED INDEX index1 ON [dbo].[Pub_Consult1]
  10. (
  11. [Directory] ASC,[ApplicationId] ASC
  12. )
  13. CREATE NONCLUSTERED INDEX idnex1 ON [dbo].[Review_Aid_UseTraining_Record]
  14. (
  15. [AidReferralId] ASC,[ApplicationId] ASC
  16. )

索引创建后,再次执行查询,原以为可提高效率,没想到我勒个去,还是要30几秒,几乎没什么改善,优化引擎顾问有时候也会失灵,在这里只是给大家演示有这种解决方案去解决问题,有时候还是靠谱的,只是这次不靠谱。没办法,只有打开函数仔细瞅瞅,再结合上面的查询计划详细图,删除先前创建的索引,然后创建了如下索引:

  1. CREATE NONCLUSTERED INDEX index1 ON dbo.Report_AdapterAssessment_Aid
  2. (
  3. AdapterAssessmentId ASC, ProductDirAId  ASC
  4. )
  5. CREATE NONCLUSTERED INDEX index1 ON dbo.Report_AdapterAssessment
  6. (
  7. ConsultId ASC
  8. )

再次执行查询

 

好了,只需3.5秒,差不多提高10倍速度,看来这次是凑效了哈。

再来看看查询计划是否有改变,上张图来说明下问题:

 

从上图当中我们可以看到,索引扫描不见了,只有索引查找,聚集索引查找,键查找,而且运算符开销,I/O开销都降低了很多。索引扫描(Index Scan),聚集索引扫描(Clustered Index Scan)跟表扫描(Table Scan)差不多,基本上是逐行去扫描表记录,速度很慢,而索引查找(Index Seek),聚集索引查找,键查找都相当的快。优化查询的目的就是尽量把那些带有XXXX扫描的去掉,换成XXXX查找。

这样够了吗?但是回头又想想,4000多条数据得3.5秒钟,还是有点慢了,应该还能再快点,所以决定再去修改查询。看看查询,能优化的也只有那个三个函数了。

为了看函数执行效果先删除索引,看看查询中函数f_GetAidNamebyConsult1要干的事情,截取查询中与该函数有关的子查询:

  1. select Pub_Consult1.ConsultId,AidName from (select ConsultId,dbo.f_GetAidNamebyConsult1(ConsultId) as AidName
  2. from Pub_Consult1) AidNametb inner join Pub_Consult1
  3. on AidNametb.ConsultId=Pub_Consult1.ConsultId

得到下图的结果:

 

没想到就这么点数据竟然要46秒,看来这个函数真的是罪魁祸首。

该函数的具体代码就不贴出来了,而且该函数里面还欠套的另外一个函数,本身函数执行起来就慢,更何况还函数里子查询还包含函数。其实根据几相关联的表去查询几个字段,并且把一个字段的值合并到同一行,这样没必要用函数或存储过程,用子查询再加sql for xml path就行了,把该函数改成如下查询:

  1. with cte1 as
  2. (
  3. select A.AdapterAssessmentId,case when B.AidName is null then A .AidName else B.AidName end AidName
  4. from Report_AdapterAssessment_Aid as A left join Pub_ProductDir as B
  5. on A.ProductDirAId=B.ProductDirAId
  6. ),
  7. cte2 as
  8. (
  9. --根据AdapterAssessmentId分组并合并AidName字段值
  10. select AdapterAssessmentId,(select AidName+',' from cte1
  11. where AdapterAssessmentId= tb.AdapterAssessmentId
  12. for xml path(''))as AidName
  13. from cte1 as tb
  14. group by AdapterAssessmentId
  15. ),
  16. cte3 as
  17. (
  18. select ConsultId,LEFT(AidName,LEN(AidName)-1) as AidName
  19. from
  20. (
  21. select Pub_Consult1.ConsultId,cte2.AidName from Pub_Consult1,Report_AdapterAssessment,cte2
  22. where Pub_Consult1.ConsultId=Report_AdapterAssessment.ConsultId
  23. and Report_AdapterAssessment.AdapterAssessmentId=cte2.AdapterAssessmentId
  24. and  Report_AdapterAssessment.AssessTuiJian is null
  25. ) as tb)

这样查询出来的结果在没有索引的情况下不到1秒钟就行了。再把主查询写了:

  1. select distinct  Pub_AidBasicInformation.AidBasicInfoId,
  2. Pub_AidBasicInformation.UserName,
  3. Pub_AidBasicInformation.District,
  4. Pub_AidBasicInformation.Street,
  5. Pub_AidBasicInformation.Community,
  6. Pub_AidBasicInformation.DisCard,
  7. Pub_Application.CreateOn AS AppCreateOn,
  8. Pub_User.UserName as DepartmentUserName,
  9. Pub_Consult1.ConsultId,
  10. Pub_Consult1.CaseId,
  11. Clinicaltb.Clinical,
  12. cte3.AidName,
  13. Pub_Application.IsUseTraining,
  14. Pub_Application.ApplicationId,
  15. tab.num
  16. from   Pub_Consult1
  17. INNER JOIN Pub_Application ON Pub_Consult1.ApplicationId = Pub_Application.ApplicationId
  18. INNER JOIN Pub_AidBasicInformation ON Pub_Application.AidBasicInfoId = Pub_AidBasicInformation.AidBasicInfoId
  19. INNER  JOIN(select ConsultId,dbo.f_GetClinical(ConsultId) as Clinical
  20. from Pub_Consult1) Clinicaltb on Clinicaltb.ConsultId=Pub_Consult1.ConsultId
  21. left join (select distinct ApplicationId, sum(TraniningNumber) as num from dbo.Review_Aid_UseTraining_Record
  22. where  AidReferralId is null
  23. group by  ApplicationId) tab
  24. on tab.ApplicationId=Pub_Consult1.ApplicationId
  25. left JOIN cte3 on cte3.ConsultId=Pub_Consult1.ConsultId
  26. LEFT OUTER JOIN Pub_User ON Pub_Application.ReviewUserId = Pub_User.UserId
  27. where Pub_Consult1.Directory = 0
  28. order by Pub_Application.CreateOn desc

这样基本上就完事了,在没有建立索引的情况下需要8秒钟,比没索引用函数还是快了27秒。

 

把索引放进去,就只需1.6秒了,比建立索引用函数而不用子查询和sql for xml path快了1.9秒。

 

查询里面还有个地方用了函数,估计再优化下还能提高执行效率,因为时间有限再加上篇幅有点长了,在这里就不多讲了。

最后做个总结吧,查询优化不外乎以下这几种办法:

1:增加索引或重建索引。通常在外键,连接字段,排序字段,过滤查询的字段建立索引,也可通过数据库引擎优化顾问提供的信息去建索引。有时候当你创建索引时,会发现查询还是按照索引扫描或聚集索引扫描的方式去执行,而没有去索引查找,这时很可能是你的查询字段和where条件字段没有全部包含在索引字段当中,解决这个问题的办法就是多建立索引,或者在创建索引时Include相应的字段,让索引字段覆盖你的查询字段和where条件字段。

2:调整查询语句,前提要先看懂别人的查询,搞清楚业务逻辑。

3:表分区,大数据量可以考虑。

4:提高服务器硬件配置。

记一次苦逼的SQL查询优化的更多相关文章

  1. 一次苦逼的SQL注入

    0x01: 偶一打点,看到一个可爱的系统-. 1.通过F12 把链接提出来仔细瞅瞅- 2.看见id,果断测注入- 感觉有戏 嗯? 啥数据库连接出错,啥意思??? (其实,这是运维做的混淆..) 3.这 ...

  2. 引用:初探Sql Server 执行计划及Sql查询优化

    原文:引用:初探Sql Server 执行计划及Sql查询优化 初探Sql Server 执行计划及Sql查询优化 收藏 MSSQL优化之————探索MSSQL执行计划 作者:no_mIss 最近总想 ...

  3. 从苦逼到牛逼,详解Linux运维工程师的打怪升级之路

    做运维也快四年多了,就像游戏打怪升级,升级后知识体系和运维体系也相对变化挺大,学习了很多新的知识点. 运维工程师是从一个呆逼进化为苦逼再成长为牛逼的过程,前提在于你要能忍能干能拼,还要具有敏锐的嗅觉感 ...

  4. Pylons安装苦逼之路

    本文介绍一下我在安装pylons的过程中出现的一些错误和解决办法,当然这些都是不完全版. 1.在Serve1(服务器Python版本2.4.3)上面装环境的时候总是出现with_statement有关 ...

  5. PHP项目的“苦逼”经历与思考

    PHP项目的"苦逼"经历与思考 PHP零基础.但因为项目人手不够的原因,被安排到一个用户"定制"项目. 该项目是用PHP生成的统计数据报表. 而用户又有新的3个 ...

  6. SQL查询优化——数据结构设计

    本文部分内容会涉及mysql,可能在其它数据库中并不适用. 本章节仅仅针对数据库结构设计做讨论.查询优化的其它内容待续. 数据库设计及使用是WEB开发程序猿必备的一项基础技能,在大数据量和高并发场景, ...

  7. 无奈而又苦逼的二分版本号回退法定位新引入的bug!!!

    昨天測试人员和开发者都发现, 某新版本号有严重的bug.  群里已经開始嚷嚷了, 但没有谁知道是谁引入的问题.本来呢, 这个问题不应该是由我去定位, 但主管让我帮定位一下, 毕竟时间太紧急, 必须尽快 ...

  8. 30条SQL查询优化原则

    在我们平常的SQL查询中,其实我们有许多应该注意的原则,以来实现SQL查询的优化,本文将为大家介绍30条查询优化原则. 首先应注意的原则 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 wher ...

  9. 搞IT的技术人员为什么会如此苦逼

    http://www.cnblogs.com/springmvc-hibernate/archive/2012/05/10/2493733.html ————————————————————————— ...

随机推荐

  1. Nginx下实现pathinfo及ThinkPHP的URL Rewrite模式支持

    打开Nginx的配置文件 /usr/local/nginx/conf/nginx.conf 一般是在这个路径,根据你的安装路径可能有所变化.如果你配置了vhost,而且只需要你这一个vhost支持pa ...

  2. MD5的使用

    /******************************************************************************* * keyBean 类实现了RSA D ...

  3. 转载:开发者眼中最好的 22 款 GUI 测试工具

    对于很多同学来说gui程序的测试是一个难点,所以我从网上转载了一篇关于gui测试的一篇文章,里面罗列的很多工具,大家可以尝试一下学习学习. 英文原文:22 best GUI testing tools ...

  4. [51单片机] EEPROM AT24c02 [存储\读取一个字节]

    /*----------------------------------------------- 名称:IIC协议 EEPROM24c02 存数读取数据 内容:此程序用于检测EEPROM性能,测试方 ...

  5. Linux:文件权限

    使用较长格式列出文件:ls -l显示除了 '.'(当前目录),'..' 上一级目录之外的所有包含隐藏文件(Linux 下以 '.' 开头的文件为隐藏文件): ls -Al查看某一个目录的完整属性,而不 ...

  6. JavaScript 函数惰性载入

    最近看JavaScript高级程序设计,大有收获,接下来几天写一下读书笔记.之前写了一篇Ajax初步理解的随笔,里面有个函数用来创建XmlHttpRequest对象,浏览器兼容性原因,写出的代码通过大 ...

  7. Lingo 做线性规划 - Game Thoery

    Reference: <An Introduction to Management Science Quantitative Approaches to Decision Making, Rev ...

  8. cookie and session

    Session is used to save the message for the hole period of user dialogue in web service.Such as the ...

  9. iOS开发-友盟分享使用(2)

    1.友盟SDK提供功能:分享喜欢的东西到新浪微博.qq空间.为微信朋友圈等等等等社交圈. 2.友盟分享前期准备 (1)注册账号 去官网 (2)创建应用获取appkey 类似5556a53667e*** ...

  10. Java插件开发-取插件下的某个文件

    //找到插件所在处 Bundle bundle = Activator.getDefault().getBundle(); //根据插件转义成URL路径 URL url = FileLocator.t ...