时不待我 天道酬勤

没有多少时间可以虚度了....

导航

公文流转SQL优化日志四

Posted on 2011-07-06 17:14  jadesun  阅读(329)  评论(0编辑  收藏  举报

这两天事情多,没有把这个优化的贴子继续下去。由于集团那边对性能的要求有所提高,所以继续对该功能做优化。

我很认同黄老的想法。第一个是用冗余设计来提升性能。我补充一些,就是做冗余设计时要小心变成过度设计,从而消耗了成本。最近也在想扩展性、性能、成本之间平衡的事,有一个疑问,我们到底要做到什么程度才够?

在项目的后期,大幅度的修改表结构和DA的代码,不利于验收。但这时候我们可以把一些优化的思想沉淀下来,给新的项目参考。所以海航和中航油的优化,都是我们很好的交流媒介。黄老可要继续跟贴啊,也欢迎其它同学参与进来。

继续公文跟踪本部门公文的查询优化:

 

上回说到四条TOP 1语句的性能影响,引用我询问资料的解释:

在使用TOP 1的时候,嵌套循环一般适用于联系的两个表,一个表的数据较大,而另一个表的数据较小的情况,如果查询匹配的值出现在扫描的前端,则在取TOP 1的情况下,是符合嵌套循环联系的使用条件的,但当匹配的数据出现在扫描的后端,或者是基本上没有匹配的数据时,则嵌套循环要扫描完成两个大表,也正是因为这种情况,导致了TOP 1的效率慢很多。

果断放弃TOP 1的使用方案,重新整体修改SQL语句,修改如下:

DBCC FREEPROCCACHE

DBCC DROPCLEANBUFFERS

SET STATISTICS IO ON

SET STATISTICS TIME ON

SELECT TOP 10 a.cniId, cnvcTitle, cndSendTime, cnvcSecretaryKind, cnvcSecName, cnvcSendEname, cnvcSendCname, cnvcSendOrganCname, cnvcSendOrganId, cnvcSecretaryKind, cnvcEndflag,

      cnvcFileType, tbTempB.cnvcLeadOrganCname,tbTempA.cnvcSpeedName,tbTempA.cnvcSpeed,tbTempA.cndSpeedTime,'' AS OweStatus,cnvcFileName

FROM tbCommonPart a JOIN

        (SELECT a.cniCommonPartId,b.cnvcSpeedName,b.cnvcSpeed,b.cndSpeedTime FROM (

            SELECT cniCommonPartId,cniID=MAX(cniID) FROM tbApproval GROUP BY cniCommonPartId) a JOIN tbApproval b ON a.cniID = b.cniID

        ) tbTempA ON a.cniId = tbTempA.cniCommonPartId

    JOIN

        (SELECT a.cniCommonPartId,b.cnvcLeadOrganCname FROM (

            SELECT cniCommonPartId,cniID=MIN(cniID) FROM tbApproval GROUP BY cniCommonPartId) a JOIN tbApproval b ON a.cniID = b.cniID

        ) tbTempB ON a.cniId = tbTempB.cniCommonPartId

    WHERE cnvcSendOrganId IN (SELECT cniNodeID FROM tbCompany WHERE (cnvcOrganID LIKE '%0-1-%'))

      AND a.cniId IN

          (SELECT DISTINCT (tbApproval.cniCommonPartId)

         FROM tbCommonPart INNER JOIN

               tbApproval ON tbCommonPart.cniId = tbApproval.cniCommonPartId

         WHERE tbCommonPart.cnvcDeleteFlag = '0' AND

               tbApproval.cnvcSendEname <> '' AND cnvcReturnFlag = '0')  AND (cnvcTitle LIKE '%%')

     AND (DATEDIFF(day, cndSendTime, CAST('2011-6-13' AS DateTime)) >= 0 AND DATEDIFF(day,  cndSendTime, CAST('2011-3-15' AS DateTime)) <= 0)

    ORDER BY cnvcSpeed DESC,cndSpeedTime asc, cnvcSecretaryKind DESC, cndSendTime DESC

SET STATISTICS IO OFF

SET STATISTICS TIME OFF

 

接着采用SQL SERVER 2005的新特性(还好把数据库升级到了2005),非键列索引的特性。一些描述如下,需要深入了解的同学可以搜索一下

SQL Server 2005 中,可以通过将非键列添加到非聚集索引的叶级别来扩展非聚集索引的功能。通过包含非键列,可以创建覆盖更多查询的非聚集索引。这是因为非键列具有下列优点:

    * 它们可以是不允许作为索引键列的数据类型。

    * 在计算索引键列数或索引键大小时,数据库引擎不考虑它们。

  当查询中的所有列都作为键列或非键列包含在索引中时,带有包含性非键列的索引可以显著提高查询性能。这样可以实现性能提升,因为查询优化器可以在索引中找到所有列值;不访问表或聚集索引数据,从而减少磁盘 I/O 操作。

  说明:第一:只能是针对非聚集索引;第二:比起复合索引是有性能上的提升的,因为索引的大小变小了;

 

tbApproval表的四个字段加入cniCommonPartID的非键列。 CREATE INDEX IX_tbApproval_CommonPartID ON tbApproval(cniCommonPartID) INCLUDE(cnvcLeadOrganCname,cnvcSpeedName,cnvcSpeed,cndSpeedTime)

 

通过返回10条数据,优化前后的语句执行效率如下,小红可以在后面做一下数据准确性的验证,如果没有问题就正式改造本部门查询的功能。

 

未优化的SQL语句如下:

DBCC FREEPROCCACHE

DBCC DROPCLEANBUFFERS

SET STATISTICS IO ON

SET STATISTICS TIME ON

SELECT top 10 cniId, cnvcTitle, cndSendTime, cnvcSecretaryKind, cnvcSecName, cnvcSendEname, cnvcSendCname,

      cnvcSendOrganCname, cnvcSendOrganId, cnvcSecretaryKind, cnvcEndflag,

      cnvcFileType,

          (SELECT TOP 1 cnvcLeadOrganCname FROM tbApproval AS tbA WHERE tbCommonPart.cniId = tbA.cniCommonPartId)  AS cnvcLeadOrganCname,

          (SELECT TOP 1 cnvcSpeedName FROM tbApproval AS tbA WHERE tbCommonPart.cniId = tbA.cniCommonPartId ORDER BY tbA.cniId DESC)  AS cnvcSpeedName,

          (SELECT TOP 1 cnvcSpeed FROM tbApproval AS tbA WHERE tbCommonPart.cniId = tbA.cniCommonPartId ORDER BY tbA.cniId DESC)  AS cnvcSpeed,

          (SELECT TOP 1 cndSpeedTime FROM tbApproval AS tbA WHERE tbCommonPart.cniId = tbA.cniCommonPartId ORDER BY tbA.cniId DESC)  AS cndSpeedTime,

          '' AS OweStatus,

       cnvcFileName

FROM tbCommonPart

WHERE cnvcSendOrganId IN (SELECT cniNodeID FROM tbCompany WHERE (cnvcOrganID LIKE '%0-1-%'))

      AND cniId IN

          (SELECT DISTINCT (tbApproval.cniCommonPartId)

         FROM tbCommonPart INNER JOIN

               tbApproval ON tbCommonPart.cniId = tbApproval.cniCommonPartId

         WHERE tbCommonPart.cnvcDeleteFlag = '0' AND

               tbApproval.cnvcSendEname <> '' AND cnvcReturnFlag = '0')  AND (cnvcTitle LIKE '%%')

     AND (DATEDIFF(day, cndSendTime, CAST('2011-6-13' AS DateTime)) >= 0 AND DATEDIFF(day,  cndSendTime, CAST('2011-3-15' AS DateTime)) <= 0)

    ORDER BY cnvcSpeed DESC,cndSpeedTime asc, cnvcSecretaryKind DESC, cndSendTime DESC

SET STATISTICS IO OFF

SET STATISTICS TIME OFF

 

 

优化前的数据查询结果:

DBCC 执行完毕。如果DBCC 输出了错误信息,请与系统管理员联系。

DBCC 执行完毕。如果DBCC 输出了错误信息,请与系统管理员联系。

(10 行受影响)

'tbApproval'。扫描计数232553,逻辑读取752747 次,物理读取209 次,预读7721 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

'Worktable'。扫描计数0,逻辑读取0 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

'tbCommonPart'。扫描计数13,逻辑读取2735 次,物理读取7 次,预读2873 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

'tbCompany'。扫描计数1,逻辑读取557 次,物理读取0 次,预读564 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

SQL Server 执行时间:

   CPU 时间= 1844 毫秒,占用时间= 3560 毫秒。

SQL Server 执行时间:

   CPU 时间= 0 毫秒,占用时间= 1 毫秒。

 

 

优化之后的数据查询结果:

DBCC 执行完毕。如果DBCC 输出了错误信息,请与系统管理员联系。

DBCC 执行完毕。如果DBCC 输出了错误信息,请与系统管理员联系。

(10 行受影响)

'tbCommonPart'。扫描计数22,逻辑读取2600 次,物理读取9 次,预读2728 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

'tbApproval'。扫描计数14,逻辑读取4522 次,物理读取9 次,预读2260 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

'Worktable'。扫描计数0,逻辑读取0 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

'tbCompany'。扫描计数1,逻辑读取557 次,物理读取0 次,预读564 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

SQL Server 执行时间:

   CPU 时间= 906 毫秒,占用时间= 2427 毫秒。

SQL Server 执行时间:

   CPU 时间= 0 毫秒,占用时间= 1 毫秒。

 

扫描次数、逻辑读、预读都大幅度的降低。CPU消耗时间也下降了一秒,占用时间由于输出的IO量相同,也只下降了一秒。总得来说,还是给数据库服务器相当减压了。