2012-07-23 69 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!你远离唯一一个认为这个功能非常缺乏的人。