需要替换where子句中的日期函数

时间:2014-03-10 10:25:31

标签: sql sql-server function query-performance

我们有一个报表功能,它使用标量函数来提取报表。我通过将函数转换为存储过程并应用最佳实践来调整它。 我发现我们在where子句中使用与日期相关的函数,这些函数由于遵循以下条件而不可被攻击

AND dbo.mefn_GetDateDifference(c.date_of_birth,GETDATE(),'YEAR')  >=66

函数dbo.mefn_GetDateDifference是计算年龄的经典示例,我们首先比较年份和月份以给出结果集。

由于我在函数内使用c.date_of_birth,因此没有使用date_of_birth上的索引。 有人有更好的想法来优化这个吗?

假设我的一位客户生日是'1939-05-20 00:00:00.000' 如果我们说

SELECT DATEDIFF(year,'1939-05-20 00:00:00.000',GETDATE()) 

它给了我75年

但是函数dbo.mefn_GetDateDifference按照以下逻辑将其计算为74

DECLARE
    @adStartDate datetime,
    @adEndDate datetime,
    @asDateDifferenceType as varchar(20)

SET  @adStartDate='1939-05-20 00:00:00.000'
SET  @adEndDate=getdate()


DECLARE @iDateDifference int

set @iDateDifference = 0

 select @iDateDifference =   CASE when month(@adEndDate) > month(@adStartDate)  THEN DateDiff(year, @adStartDate, @adEndDate)   
                              when month(@adStartDate) = month(@adEndDate) and day(@adEndDate) >= day(@adStartDate) THEN DateDiff(year, @adStartDate, @adEndDate)   
                              ELSE DateDiff(year, @adStartDate, @adEndDate) - 1 END  



PRINT @iDateDifference

所以由于这种差异,我正在使用功能,但为了更好的性能,我不想使用它

2 个答案:

答案 0 :(得分:0)

您是否首先创建一个当前日期减去67或66的变量,然后在where子句中使用该变量?

DECLARE @CalculateDate DATETIME
SET @CalculateDate = DATEADD(year, -66, GETDATE())

SELECT * 
FROM (....)
WHERE date_of_birth <= @CalculateDate 

编辑:对ADK的回应 @CalculateDate的价值现在是1948年3月11日上午8:58 在您的示例中,我可以看到您使用自定义函数的原因,但在我的示例中,我认为这不是必需的。

当我们用日期本身计算时,我们不必计算生日年份&#39;因为我们已经确定了正确的约会对象,我们可以从中计算出是否有66岁或以上的人。

答案 1 :(得分:0)

正如您已经提到的,您遇到的问题是系统必须遍历每条记录以计算dbo.mefn_GetDateDifference(c.date_of_birth,GETDATE(),'YEAR'),以便找出是否匹配。虽然在某些情况下您可以尝试将公式放在计算字段中并通过在其上放置索引来预先计算公式;由于函数将GetDate()作为参数,并且在查询运行期间它被视为常量,因此每次运行都会有所不同。

正如GercoOnline已经提到的那样,您应该尝试切换情况,以便您可以将查询编写为WHERE c.myField <= dbo.SomeFunction(@var1, @var2),其中@varN是常数&#39;在查询期间(例如GetDate()将起作用,NewId()赢了,也不会列的值)。

一旦达到这一点,查询优化工具将能够使用c.myField上的索引更快地获得结果。 &#39;反相&#39;该函数可能有点挑战,因为似乎一系列输入日期可能会导致相同的结果(年龄)。因此,我认为最好创建两个函数,一个计算将导致给定年龄的较低日期,另一个计算将导致相同年龄的较高日期。您需要根据要求调整查询。如果您想拥有66岁的所有客户,那么它将类似于:

WHERE c.date_of_birth BETWEEN dbo.mefn_GetMinBirthDate(GETDATE(),'YEAR', 66) AND dbo.mefn_GetMaxBirthDate(GETDATE(),'YEAR', 66)

如果您更愿意需要66岁以上的人,那就是:

WHERE c.date_of_birth < dbo.mefn_GetMinBirthDate(GETDATE(),'YEAR', 66)

或年龄小于66岁的人

WHERE c.date_of_birth > dbo.mefn_GetMaxBirthDate(GETDATE(),'YEAR', 66)