SQL Server中存儲過程慢于SQL語句直接執行的原因
SQL Server數據庫中,存儲過程的執行總是要比SQL語句直接執行要慢,這究竟是為什么呢?本文將帶您尋找答案。
在SQL Server中有一個叫做 “Parameter sniffing”的特性。SQL Server在存儲過程執行之前都會制定一個執行計劃。在上面的例子中,SQL在編譯的時候并不知道@thedate的值是多少,所以它在執行執行計劃的時候就要進行大量的猜測。假設傳遞給@thedate的參數大部分都是非空字符串,而FACT表中有40%的thedate字段都是null,那么SQL Server就會選擇全表掃描而不是索引掃描來對參數@thedate制定執行計劃。全表掃描是在參數為空或為0的時候最好的執行計劃。但是全表掃描嚴重影響了性能。
假設你第一次使用了Exec pro_ImAnalysis_daily @thedate=’20080312’那么SQL Server就會使用20080312這個值作為下次參數@thedate的執行計劃的參考值,而不會進行全表掃描了,但是如果使用@thedate=null,則下次執行計劃就要根據全表掃描進行了。
有兩種方式能夠避免出現“Parameter sniffing”問題:
<!--(1)通過使用declare聲明的變量來代替參數:使用set @variable=@thedate的方式,將出現@thedate的sql語句全部用@variable來代替。
<!--(2) 將受影響的sql語句隱藏起來,比如:
<!-- a) 將受影響的sql語句放到某個子存儲過程中,比如我們在@thedate設置成為今天后再調用一個字存儲過程將@thedate作為參數傳入就可以了。
<!-- b) 使用sp_executesql來執行受影響的sql。執行計劃不會被執行,除非sp_executesql語句執行完。
<!-- c) 使用動態sql(”EXEC(@sql)”來執行受影響的sql。
采用(1)的方法改造例子中的存儲過程,如下:
代碼:
ALTER PROCEDURE [dbo].[pro_ImAnalysis_daily]@var_thedate VARCHAR(30) ASBEGIN declare @THEDATE VARCHAR(30) IF @var_thedate IS NULL BEGIN SET @var_thedate=CONVERT(VARCHAR(30),GETDATE()-1,112); END SET @THEDATE=@var_thedate; DELETE FROM RPT_IM_USERINFO_DAILY WHERE THEDATE=@THEDATE; INSERT RPT_IM_USERINFO_DAILY (THEDATE,ALLUSER,NEWUSER) SELECT AA.THEDATE,ALLUSER,NEWUSER FROM ( ( SELECT THEDATE,COUNT(DISTINCT USERID) ALLUSER FROM FACT WHERE THEDATE=@THEDATE GROUP BY THEDATE ) AA LEFT JOIN (SELECT THEDATE,COUNT(DISTINCT USERID) NEWUSER FROM FACT T1 WHERE NOT EXISTS( SELECT 1 FROM FACT T2 WHERE T2.THEDATE<@THEDATE AND T1.USERID=T2.USERID) AND T1.THEDATE=@THEDATE GROUP BY THEDATE ) BB ON AA.THEDATE=BB.THEDATE);GO
【編輯推薦】