2012-07-23 17 views
1
CREATE function [dbo].[fn_GetDateOnly](@dateWithTime datetime) 
    returns datetime WITH SCHEMABINDING 
as 
begin 
    return DATEADD(DAY, DATEDIFF(DAY, 0, @dateWithTime), 0) 
end 


SELECT stuff, count(ff.id) 
FROM dbo.foo AS ff 
where 
--DATEADD(DAY, DATEDIFF(DAY, 0, ff.startDate), 0) <= @curdate --6700ms 
--dbo.fn_GetDateOnly(ff.startDate) <= @curdate --9300ms 
group by stuff 
order by stuff desc 

爲什麼調用fn_getdateonly比內聯慢得多? SQL不內聯函數調用嗎?標量值函數比預期的要慢

+0

如果可能的話,您應該儘量避免圍繞列進行任何類型的函數調用。 'WHERE ff.startDate 2012-07-23 15:45:07

回答

3

表達對列

首先,不管你用什麼樣的功能,把它在WHERE子句中或JOIN條件中對錶中的一列是次優的。做一個常數的數學和比較。您的WHERE子句中應該是這樣的:

ff.startDate < DateAdd(day, 1, @curdate) -- if @curdate has time portion removed 
ff.startDate < DateAdd(day, 1, dbo.fn_GetDateOnly(@curdate)) -- if @curdate has time 

對於在給定日期一般尋找項目,使用這個模式:

WHERE 
    DateCol >= '20120901' 
    AND DateCol < '20120902' 

把任何功能上等號的另一側列,這應該是孤獨的。它可以幫助你查找如何使一個表達式SARGABLE。如果列必須位於兩側,則將所有表達式放在執行計劃中「左」輸入的一側(其數據先到達,LOOP JOIN的外循環或一個LOOP JOIN的「表」一側哈希連接)。例如,如果你想做到這一點:

WHERE dbo.fn_getDateOnly(A.DateCol) = dbo.fn_getDateOnly(B.DateCol) 

然後假設A.DateCol至上在執行計劃中,切換到:

WHERE 
    B.DateCol >= DateAdd(day, DateDiff(day, 0, A.DateCol), 0) 
    AND B.DateCol < DateAdd(day, DateDiff(day, 0, A.DateCol), 0) 

(或使用內嵌版本函數如下,但我覺得它是尷尬的,所以沒有額外的價值是間接的)。

如果這種查詢將在所涉及的表上頻繁進行,那麼可能需要重新設計一些重新設計,或者添加一個刪除了時間部分(可能被索引)的持久計算列,以便將日期時間轉換爲單獨的日期和時間字段,或將其存儲爲開頭的日期(如果您確實不需要日期時間)。

注意:當然,對dbo.fn_getDateOnly的引用可以簡單地替換爲DateAdd(day, DateDiff(day, 0, DateCol), 0)。此外,如果值爲datetime數據類型,則只需執行DateCol + 1而不是使用DateAdd(不過要小心,因爲這對於SQL 2008和更高版本中的date數據類型不起作用)。

的UDF

的直列能力對於SELECT子句中的函數的性能,SQL Server只內聯「內聯函數」,而不是標量函數。改變你的函數返回一個單行記錄CREATE FUNCTION ... RETURNS TABLE AS RETURN (SELECT ...),並使用它,像這樣:

SELECT 
    (SELECT DateValue FROM dbo.fn_GetDateOnly(Col)), 
    ... 

這真的是不超過一個參數化的視圖不同。

在WHERE子句中使用這個內聯版本會變得笨拙。做DateDiff幾乎更好。在SQL 2008中,當然只是使用Convert(date, DateCol),但仍然遵循關於在哪裏放置計算表達式(與列中等號相反的一側)的規則。

一定要投票inline scalar UDFs on Microsoft Connect!你遠離唯一一個認爲這個功能非常缺乏的人。