2017-03-28 61 views
2

我有(用於一些奇怪的原因)結束了一個視圖需要約30秒編譯,但< 3秒運行。重複使用執行計劃查詢與不同的WHERE子句的視圖

這是一個視圖,坐在一大堆嵌套視圖的頂部,每個嵌套視圖都有許多層序的CTE。底層數據集並不是那麼大,我想象的是爲什麼查詢最終運行速度非常快。

如果我們總是閱讀整個表格,那很好 - 第一個查詢會很慢,而且以後每次點擊都會很好。 不幸的是,訪問代碼將要用日期窗口讀取它。

SELECT * FROM myView WHERE date BETWEEN 'foo' AND 'bar'

您運行的第一次,它需要30秒編譯Exec的計劃;第二次運行1-3秒。

是否有防止重新編譯? 我認識到它可能會導致最終的執行計劃效率不高,因爲它針對不同的子句進行了優化,但數據非常均勻,所以我不希望它太糟糕,並且30秒的編譯時間是太痛苦了。


我已經通過像thesepages頁看了。但沒有太多的立即跳出作爲我有關的,並沒有似乎並沒有達到我的目標(雖然我可以很容易地錯過了一些東西)位


編輯:

STATISTICS TIME ON輸出對於一個類似的視圖,需要約6分鐘才能在寒冷的時候運行,或者在預編譯時運行約1/2秒(實際上,具體來說,它是從嵌套中的一層下來的視圖中的一個視圖)。

SELECT * FROM myView WHERE date_incurred < '2017-02-20' 

Run with param = '2017-02-20' 

SQL Server parse and compile time: CPU time = 5008 ms, elapsed time = 5184 ms. 
SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. 
(77 row(s) affected) 
SQL Server Execution Times: CPU time = 452 ms, elapsed time = 772 ms. 

Run with param = '2017-02-20' 

SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. 
SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. 
(77 row(s) affected) 
SQL Server Execution Times: CPU time = 437 ms, elapsed time = 582 ms. 

Run with param = '2017-02-21' 

SQL Server parse and compile time: CPU time = 4618 ms, elapsed time = 4877 ms. 
SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. 
(79 row(s) affected) 
SQL Server Execution Times: CPU time = 359 ms, elapsed time = 643 ms. 

Run with param = '2017-02-21' 

SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. 
SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. 
(79 row(s) affected) 
SQL Server Execution Times: CPU time = 483 ms, elapsed time = 559 ms. 
+0

像這樣的嵌套查詢是性能時間炸彈。看看這裏有關嵌套意見的恐怖。 https://www.simple-talk.com/sql/performance/the-seven-sins-against-tsql-performance/#seven –

+0

你的估計編譯時間如何。你可以粘貼'set statistics time on'的輸出爲不同的情況 – TheGameiswar

+0

所以我估計它是基於查詢在窗口中的執行時間,當它非常嚴重時很容易判斷差異:)將提供STATS數據。 – Brondahl

回答

0

最佳答案(IMO):sp_executesql/sp_execute

DECLARE @query int; 
EXEC sp_executesql 
    N'SELECT * FROM vlpl_combined_costs_with_invoices WHERE date_incurred < @dateParam OPTION(OPTIMIZE FOR UNKNOWN)', 
    N'@dateParam datetime', 
    '2017-09-10' 


EXEC sp_prepare @query output, 
    N'@dateParam datetime', 
    N'SELECT * FROM myView WHERE date < @dateParam OPTION(OPTIMIZE FOR UNKNOWN)'; 
EXEC sp_execute @query, '2017-09-07' 
EXEC sp_unprepare @query; 

我相信前者(sp_executesql)被認爲是更好的,雖然我不知道具體原因。


說明

當的執行計劃存儲過程產生,它是基於在時間傳遞的參數,然後將同樣的計劃是爲的後續執行重用產生相同的存儲過程,不管參數值如何 - 只需要名稱匹配[1]。

即席SQL批處理(即常規硬編碼SELECT)運行時,其執行計劃也被保存,但爲了要使用的查詢文本必須完全匹配(空格和大小寫,幷包括參數值)。

sp_execute允許獨立於參數值存儲特設查詢的查詢計劃,以便它可以在不同的參數集合中重用(我認爲其餘文本仍然必須相同) 。即它是爲我在這裏得到的確切用例而設計的。

(信用的理念和解釋去安迪·托馬斯作爲軟線)

[1]和一些其他的東西,我相信,但在這裏,這是不相關的。

0

一個可能的答案:非內聯函數

執行計劃緩存將首先使用該參數調用,然後繼續使用該計劃。

缺點 - 非常詳細定義一個函數來包裝的視圖:​​( - 將需要爲對視圖查詢的每個不同的「類型」的單獨功能(或複雜功能與許多參數,其中的許多是NULL)

0

另一種可能的答案:。使用一個存儲過程

同樣你Exec的計劃高速緩存 這個時候你沒有做詳細的表定義爲內聯表值函數,但從Sproc檢索輸出不一定和SELECT或者FUNCTION一樣。