20150804客戶sqlserver庫查詢sql執行慢的問題分析
阿新 • • 發佈:2018-11-26
執行報表反接收操作時檢查是否產生關聯報表的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