列上的表达式
首先,无论您使用什么函数,将其放在 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 的外循环或 a 的“表”侧哈希连接)。例如,如果您尝试这样做:
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
(但要小心,因为这不适date
用于 SQL 2008 及更高版本中的数据类型)。
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)
但仍然遵循关于将计算表达式放在何处的规则(在列的等号的另一侧)。
请务必在 Microsoft Connect 上为内联标量 UDF投票!您远不是唯一一个认为此功能严重缺乏的人。