最近優化一批接口,之前只優化過MySQL數據庫語句,它和SQL SERVER的差別還是很大的,記錄一下優化後時間差別最大的兩個接口。
接口一:
根據條件查詢十個分表,SQL語句處理後如下,其中查詢條件不止三個,還有一些其他。優化前:75s
select out_trade_no,total_fee, liquidator_commission_fee from [BankPayOrder_4_0] where 1=1 AND username like '%用戶名稱%' AND total_fee >=10 AND total_fee <=500 AND Convert(nvarchar(12),addtime,112) between '20190401' and '20190429' union all select out_trade_no,total_fee, liquidator_commission_fee from [BankPayOrder_4_1] where 1=1 AND username like '%用戶名稱%' AND total_fee >=10 AND total_fee <=500 AND Convert(nvarchar(12),addtime,112) between '20190401' and '20190429' union all select out_trade_no,total_fee, liquidator_commission_fee from [BankPayOrder_4_2] where 1=1 AND username like '%用戶名稱%' AND total_fee >=10 AND total_fee <=500 AND Convert(nvarchar(12),addtime,112) between '20190401' and '20190429' union all select out_trade_no,total_fee, liquidator_commission_fee from [BankPayOrder_4_3] where 1=1 AND username like '%用戶名稱%' AND total_fee >=10 AND total_fee <=500 AND Convert(nvarchar(12),addtime,112) between '20190401' and '20190429' union all select out_trade_no,total_fee, liquidator_commission_fee from [BankPayOrder_4_4] where 1=1 AND username like '%用戶名稱%' AND total_fee >=10 AND total_fee <=500 AND Convert(nvarchar(12),addtime,112) between '20190401' and '20190429' union all select out_trade_no,total_fee, liquidator_commission_fee from [BankPayOrder_4_5] where 1=1 AND username like '%用戶名稱%' AND total_fee >=10 AND total_fee <=500 AND Convert(nvarchar(12),addtime,112) between '20190401' and '20190429' union all select out_trade_no,total_fee, liquidator_commission_fee from [BankPayOrder_4_6] where 1=1 AND username like '%用戶名稱%' AND total_fee >=10 AND total_fee <=500 AND Convert(nvarchar(12),addtime,112) between '20190401' and '20190429' union all select out_trade_no,total_fee, liquidator_commission_fee from [BankPayOrder_4_7] where 1=1 AND username like '%用戶名稱%' AND total_fee >=10 AND total_fee <=500 AND Convert(nvarchar(12),addtime,112) between '20190401' and '20190429' union all select out_trade_no,total_fee, liquidator_commission_fee from [BankPayOrder_4_8] where 1=1 AND username like '%用戶名稱%' AND total_fee >=10 AND total_fee <=500 AND Convert(nvarchar(12),addtime,112) between '20190401' and '20190429' union all select out_trade_no,total_fee, liquidator_commission_fee from [BankPayOrder_4_9] where 1=1 AND username like '%用戶名稱%' AND total_fee >=10 AND total_fee <=500 AND Convert(nvarchar(12),addtime,112) between '20190401' and '20190429'
優化:
優化點一:如果username字段有索引,但是like後面第一個爲"%",查詢也不會走索引。就算username字段沒有索引,like後面第一個跟“%”和不是“%”的查詢時間差別也很大,所以最好不使用。但是這樣會導致需求變更了,導致可能查詢不到想要查的。綜合一下,另寫了一個接口,通過用戶名稱模糊查詢到用戶Id,然後使用者選擇一個用戶,將此用戶的Id傳入。
優化點二:由於查詢時,時間段是必傳字段,所以在時間字段上加上聚集索引(表之前的結構是另外兩個字段訂單號、商戶ID組成的聚集索引,實際並沒有什麼用,也發揮不了聚集索引的優點)。這裏指出,如果將時間字段設置爲非聚集索引,可能SQL SERVER並不走這個索引,只有當查詢的時間段很短的時候,纔會走時間的非聚集索引。
優化點三:設置了時間聚集索引,通過SQL SERVER的執行計劃發現,SQL SERVER走的是時間聚集索引掃描(如圖一),導致的原因是Convert將時間裝換爲字符串格式了。將Convert刪除,查詢條件改爲時間格式,通過執行計劃看到此時就走聚集索引查找(如圖二)。
圖一
圖二
優化後查詢時間1天,300ms。由於設置的是時間聚合索引,所以時間的跨度越大,耗時越久,查詢一個月的數據,耗時5s左右,因爲接口是運營做統計使用,已經到能接受的程度了。
接口二:
優化前耗時2s,SQL語句如下:
SELECT A.*, B.LableName, C.Name AS CustomerName, D.Company AS FatherName, AE.BonusActivityID, BO.ActivityName, AE.IsBonus FROM Sys_Agents A LEFT JOIN Sys_AgentLable B ON A.AgentLabelID= B.LableID LEFT JOIN AgentExtend AE ON A.AgentsID= AE.AgentsID LEFT JOIN BonusActivities BO ON AE.BonusActivityID= BO.ActivityID LEFT JOIN Agents_Customer C ON A.CustomerID= C.ID LEFT JOIN Sys_Agents D ON A.FatherId= D.AgentsID WHERE 1 = 1 and A.Company like '%衆恆科技有限公司%' AND A.IsOEM=0 AND A.AgentLevel=1 AND CONVERT ( nvarchar ( 10 ), A.EndTime, 112 ) >'20190416' AND CONVERT ( nvarchar ( 10 ), A.EndTime, 112 ) <='20191222' AND A.CustomerID=0 AND A.State=1
優化點一:like,處理方式和第一個相同。
優化點二:convert ,使用時間格式。
優化點三:Sys_Agents表的字段不需要全部返回,全部字段有50多個。(怎麼會有這麼多字段???可能是歷史原因吧)
優化點四:由於是連接查詢,不論是交叉聯接、左聯接、右聯接、內聯接都會先產生笛卡爾積,然後過濾條件。所以將Where條件之後涉及的表現查詢出來放到臨時表中,在連接查詢,減少笛卡爾積。
優化後 300ms左右。