1. 程式人生 > >20150804客戶sqlserver庫查詢sql執行慢的問題分析

20150804客戶sqlserver庫查詢sql執行慢的問題分析

執行報表反接收操作時檢查是否產生關聯報表的sql查詢。

這條指令碼在oracle庫都執行正常,執行速度可以控制在0.05s以內。


存在sql查詢效能問題的客戶情況:

1、作業系統

 阿里雲 四核、14G記憶體。資料庫伺服器分配了10個G的記憶體。



2、Sql Server設定



3、業務資料:







4、客戶出現效能的SQL

SELECT distinct r.FID AS ID,r2.FName AS SourceReport,                                   
 r2.FNumber AS SourceReportNumber,                                              
 tr.FName_l2 AS TreeName,                                            
 o.FName_l2 AS UnitName,                                             
 c.FName_l2 AS CurrencyName,                                         
 r.FPeriodType AS PeriodType,                                                   
 r.FYear AS FYear,                                                              
 r.FPeriod AS Period,                                                           
 r.FSourceType AS SourceType,                                                   
 r.FName AS ReportName,                                                         
 s.FLongNumber AS OrgLongNumber,                                                
 r.FCommittedStatus  AS CommittedStatus                                         
 FROM T_CSL_CslReport r                                                       
     INNER JOIN T_Org_Tree tr  ON r.FOrgTreeID = tr.FID                                             
     INNER JOIN T_Org_BaseUnit o  ON r.FOrgUnitID = o.FID                                              
     INNER JOIN T_BD_Currency c   ON r.FCurrencyID = c.FID                                             
 INNER JOIN T_RPT_Template t ON r.FTemplateID = t.FID                                                 
 INNER JOIN T_CSL_TempletItemList til  ON t.FID = til.FTemplateID                                               
 INNER JOIN T_CSL_TempletItemList til2                                        
     ON til.FItemNumber = til2.FItemNumber                                    
          AND til.FDataElement = til2.FDataElement                            
          AND til.FValueType = til2.FValueType                                
 INNER JOIN T_RPT_Template t2                                                 
     ON til2.FTemplateID = t2.FID                                             
 INNER JOIN T_CSL_CslReport r2                                                
     ON t2.FID = r2.FTemplateID AND r.FCurrencyID = r2.FCurrencyID        
      AND r.FYear = r2.FYear AND r.FPeriod = r2.FPeriod  AND r.FPeriodType = r2.FPeriodType     
 INNER JOIN T_CSL_RptReceived rec                                             
     ON r2.FID = rec.FReportID AND r.FOrgTreeID = rec.FOrgTreeID              
 INNER JOIN T_Org_Structure s                            
    ON r.FOrgTreeID = s.FTreeID AND r.FOrgUnitID = s.FUnitID        
 INNER JOIN T_Org_Structure s2                            
    ON rec.FOrgTreeID = s2.FTreeID AND r2.FOrgUnitID = s2.FUnitID      
 WHERE  r2.FID IN ('Dq8AAABWGky3Euos','Dq8AAABWGnS3Euos','Dq8AAABWGoK3Euos','Dq8AAABWGkK3Euos','Dq8AAABWGqG3Euos')                             
     AND CHARINDEX(s.FLongNumber  + '!', s2.FLongNumber) = 1                    
    AND ((r.FSourceType IN (2,3,4) AND r2.FSourceType IN (1,7))        
    OR ((r.FSourceType = 2 AND r2.FSourceType = 2) AND (r.FSumCalculateMethod=0 OR r.FSumCalculateMethod=2))      
      OR (r.FSourceType = 3 AND r2.FSourceType = 3)            
      OR ((r.FSourceType = 2 AND r2.FSourceType = 3) AND (r.FSumCalculateMethod=1 OR r.FSumCalculateMethod=2)) )  


症狀:執行完這條查詢需要將近半個小時。


sql分析:

1)sql的邏輯:用報表+組織+模板+專案,分別查詢出上下級兩個試圖,進行匹配過濾。

2)效能點:T_CSL_TemplateItemList作為最明細級表,此試圖的結果集龐大,特別是查詢上級試圖時,因為只能通過一些若關鍵字過濾。

3)優化點:採用子查詢方式,縮小中間結果集,提高匹配過濾效率。


優化後sql:

select rpt1.fname, rpt1.fnumber
  from (select distinct s.flongnumber, r.fname, r.fnumber
          from t_csl_cslreport r
          join t_rpt_template tmpl
            on r.ftemplateid = tmpl.fid
          join t_org_structure s
            on r.FCompanyID = s.funitid
          join t_csl_rptreceived rec
            on rec.freportid = r.fid
           and s.ftreeid = rec.forgtreeid
         where tmpl.fgroup in
 --獲取集團模板id
               (select distinct temp.fgroup
                  from t_rpt_template  temp
join t_csl_templetitemlist templist on temp.fid = templist.ftemplateid
                 where exists (select 1 from tempId tt where templist.fid = tt.fid)
                     templist.fid in (select lst2.fid
                          from (select lst.fitemnumber,
                                        lst.fvaluetype,
                                        lst.FDataElement,
                                        lst.fid
                                   from t_csl_templetitemlist lst
                                  where lst.ftemplateid in
                                        (select distinct case  when tmpl.fgrouptemplateid is null then
                                                            tmpl.fid
                                                           else
                                                            tmpl.fgrouptemplateid
                                                         end
                                           from t_csl_cslreport r
                                           join t_rpt_template tmpl
                                             on r.ftemplateid = tmpl.fid
                                          where r.fid in ('Dq8AAABLKIm3Euos'))) lst1
                          join (select lst.fitemnumber,
                                       lst.fvaluetype,
                                       lst.FDataElement,
                                       lst.fid
                                  from t_csl_templetitemlist lst
                                 where lst.ftemplateid in
				   --找到當前合併範圍分配了哪些模板,查t_csl_rptreceived表是因為有一表多報的情況
                                       (select distinct disp.fdispensesrcid
                                          from t_csl_cslreport r
                                          join t_csl_rptreceived rec
                                            on r.fid = rec.freportid
                                          join t_csl_templetdispense disp
                                            on disp.forgboundid = rec.forgtreeid
                                         where r.fid in ('Dq8AAABLKIm3Euos'))) lst2
                            on lst1.fitemnumber = lst2.fitemnumber
                           and lst1.fvaluetype = lst2.fvaluetype
                           and lst1.FDataElement = lst2.FDataElement
                           and lst1.fid <> lst2.fid )
						   )) rpt1
  join (select s.flongnumber,r.FYear ,r.FPeriod, r.FPeriodType, r.FCurrencyID, r.FSourceType
          from t_csl_cslreport r
          join t_rpt_template tmpl
            on r.ftemplateid = tmpl.fid
          join t_org_baseunit unit
            on (r.fcompanyid = unit.fid or r.forgunitid = unit.fid)
          join t_org_structure s
            on unit.fid = s.funitid
          join t_csl_rptreceived rec
            on rec.freportid = r.fid
           and s.ftreeid = rec.forgtreeid
         where r.fid in ('Dq8AAABLKIm3Euos')) rpt2
  on CHARINDEX(rpt1.FLongNumber + '!', rpt2.FLongNumber) = 1
  

第一次優化效果:中間結果集明顯縮小,已經能把執行時間縮短至兩分鐘以內。

分析得出,查詢專案id的結果集有1000多行資料,於是採用臨時表策略,將子查詢的id插入臨時表中,然後用Exists字句進行過濾。


臨時表sql:

insert  into tempId 
select lst2.fid
  from (select lst.fitemnumber,
                                        lst.fvaluetype,
                                        lst.FDataElement,
                                        lst.fid
                                   from t_csl_templetitemlist lst
                                  where lst.ftemplateid in
                                        (select distinct case  when tmpl.fgrouptemplateid is null then
                                                            tmpl.fid
                                                           else
                                                            tmpl.fgrouptemplateid
                                                         end
                                           from t_csl_cslreport r
                                           join t_rpt_template tmpl
                                             on r.ftemplateid = tmpl.fid
                                          where r.fid in ('Dq8AAABWGky3Euos','Dq8AAABWGnS3Euos','Dq8AAABWGoK3Euos','Dq8AAABWGkK3Euos','Dq8AAABWGqG3Euos'))) lst1
                          join (select lst.fitemnumber,
                                       lst.fvaluetype,
                                       lst.FDataElement,
                                       lst.fid
                                  from t_csl_templetitemlist lst
                                 where lst.ftemplateid in
				   --找到當前合併範圍分配了哪些模板,查t_csl_rptreceived表是因為有一表多報的情況
                                       (select distinct disp.fdispensesrcid
                                          from t_csl_cslreport r
                                          join t_csl_rptreceived rec
                                            on r.fid = rec.freportid
                                          join t_csl_templetdispense disp
                                            on disp.forgboundid = rec.forgtreeid
                                         where r.fid in ('Dq8AAABWGky3Euos','Dq8AAABWGnS3Euos','Dq8AAABWGoK3Euos','Dq8AAABWGkK3Euos','Dq8AAABWGqG3Euos'))) lst2
                            on lst1.fitemnumber = lst2.fitemnumber
                           and lst1.fvaluetype = lst2.fvaluetype
                           and lst1.FDataElement = lst2.FDataElement
                           and lst1.fid <> lst2.fid 

優化後的sql變成:

select  rpt1.fid as id, rpt2.FName AS SourceReport,      
 rpt2.FNumber AS SourceReportNumber,                                              
 rpt1.treeName AS TreeName,                                            
 rpt1.unitName AS UnitName,                                             
 rpt2.currencyName AS CurrencyName,                                         
 --rpt1.FPeriodType AS PeriodType,                                                   
 --rpt1.FYear AS FYear,                                                              
 --rpt1.FPeriod AS Period,                                                           
 rpt1.FSourceType AS SourceType,                                                   
 rpt1.FName AS ReportName,                                                         
 rpt1.FLongNumber AS OrgLongNumber,                                                
 rpt1.FCommittedStatus  AS CommittedStatus 
  from (select distinct s.flongnumber, r.fname, r.fnumber, r.fid, t.FName_L2 treeName, u.FName_L2 unitName, 
	s.FUnitId, r.FSourceType, r.FCommittedStatus, r.FCurrencyID, r.FSumCalculateMethod
          from t_csl_cslreport r
          join t_rpt_template tmpl
            on r.ftemplateid = tmpl.fid
		inner join T_ORG_BaseUnit u 
			on (r.FCompanyID = u.fid or r.FOrgUnitID = u.fid)
          inner join t_org_structure s
            on u.fid = s.FUnitId
		inner join t_org_tree t
			 on s.FTreeId = t.FID
          inner join t_csl_rptreceived rec
            on rec.freportid = r.fid
           and s.ftreeid = rec.forgtreeid
         where tmpl.fgroup in
 --獲取集團模板id
               (select distinct temp.fgroup
                  from t_rpt_template  temp
				  join t_csl_templetitemlist templist on temp.fid = templist.ftemplateid
                 where exists (select 1 from tempId tt where templist.fid = tt.fid)
				)
		        and r.FSourceType in (2,3,4) and r.FYear = 2015 and r.FPeriod = 6 and r.FPeriodType =3
	   ) rpt1
  join (select s.flongnumber, r.FNumber, r.FName, c.FName_L2 as currencyName, r.FCurrencyID, r.FSourceType,r.FSumCalculateMethod
          from t_csl_cslreport r
          join t_rpt_template tmpl
            on r.ftemplateid = tmpl.fid
          join t_org_baseunit unit
            on (r.fcompanyid = unit.fid or r.forgunitid = unit.fid)
          join t_org_structure s
            on unit.fid = s.funitid
          join t_csl_rptreceived rec
            on rec.freportid = r.fid
           and s.ftreeid = rec.forgtreeid
		 INNER JOIN T_BD_Currency c   ON r.FCurrencyID = c.FID   
         where r.fid in ('Dq8AAABWGky3Euos','Dq8AAABWGnS3Euos','Dq8AAABWGoK3Euos','Dq8AAABWGkK3Euos','Dq8AAABWGqG3Euos')) rpt2
  on CHARINDEX(rpt1.FLongNumber + '!', rpt2.FLongNumber) = 1 
  where 1=1 and rpt1.FCurrencyID = rpt2.FCurrencyID 
   AND ((rpt1.FSourceType IN (2,3,4) AND rpt2.FSourceType IN (1,7))        
    OR ((rpt1.FSourceType = 2 AND rpt2.FSourceType = 2) AND (rpt1.FSumCalculateMethod=0 OR rpt1.FSumCalculateMethod=2))      
      OR (rpt1.FSourceType = 3 AND rpt2.FSourceType = 3)            
      OR ((rpt1.FSourceType = 2 AND rpt2.FSourceType = 3) AND (rpt1.FSumCalculateMethod=1 OR rpt1.FSumCalculateMethod=2)) ) 
  order by rpt1.FSourceType

 


經測試:完成查詢1S之內。