简体   繁体   中英

What is the most accurate way of using DATEDIFF in SQL Server?

I have two computed columns (MonthsInService and YearsInService) with the following expressions.

MonthsInService = (datediff(month,[DateEngaged],getdate()))

YearsInService = (datediff(month,[DateEngaged],getdate())/(12))

Now if for example DateEngaged = 2012-April-09 and getdate() is 2013-April-08, MonthsInService returns 12 and YearsInService is 1.

My application requires that YearsInService be Zero since there is still one day to go before the employees first Anniversary.

Am not even sure how to best handle the MonthsInService column since months have varying number of days.

Unfortunately, DATEDIFF computes the number of transitions of the element, rather than the usual, human intuition of the difference between two dates (eg DATEDIFF(year,'20121231','20130101') is 1, even though not many people would say that there's a difference of a year).

The solution I'd use is a bit repetitive, but doesn't need a separate function, and always gets eg leap years correct:

declare @T table (
    DateEngaged datetime not null,
    MonthsInService as CASE
        WHEN DATEADD(month,DATEDIFF(month,DateEngaged,GETDATE()),DateEngaged) > GETDATE()
            THEN DATEDIFF(month,DateEngaged,GETDATE()) - 1
            ELSE DATEDIFF(month,DateEngaged,GETDATE())
        END,
    YearsInService as CASE
        WHEN DATEADD(year,DATEDIFF(year,DateEngaged,GETDATE()),DateEngaged) > GETDATE()
            THEN DATEDIFF(year,DateEngaged,GETDATE()) - 1
            ELSE DATEDIFF(year,DateEngaged,GETDATE())
        END
)

insert into @T (DateEngaged) values ('20120409'),('20120408')

select * from @T

Produces:

DateEngaged             MonthsInService YearsInService
----------------------- --------------- --------------
2012-04-09 00:00:00.000 11              0
2012-04-08 00:00:00.000 12              1

It works by asking "If we take the naive answer produced by DATEDIFF , does it given an answer that's too high by 1?" - and if so, we just subtract one from the answer it gives. DATEDIFF should only ever be over by 1.

Via using day you can reach the result:

select 
datediff(month,'2012-April-09','2013-April-08') MonthsInService
,datediff(day,'2012-April-09','2013-April-08')/365 YearsInService

Output :

12  0  

or use function for maximum precision:

CREATE FUNCTION [dbo].[getFullYears] 
(
    @dateX datetime,
    @dateY datetime
)  
RETURNS int
AS  
BEGIN 
    DECLARE @y int
    SET @y =DATEDIFF(year,@dateX,@dateY)
    IF (@dateY < DATEADD(year, @y, @dateX)) SET @y = @y -1
    RETURN @y
END

select dbo.getFullYears('2012-April-09','2013-April-09') --1
select dbo.getFullYears('2012-April-09','2013-April-08') --0

For months calculation you can refer here: Calculating number of full months between two dates in SQL

DATEDIFF(DAY, CONVERT(date, dtmDOB),
CONVERT(date, GETDATE()))*(12.0/365.25)),1))
AS TotalMonths,

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM