How to calculate age (in years) based on Date of Birth and getDate()

前端 未结 30 2091
死守一世寂寞
死守一世寂寞 2020-11-22 02:08

I have a table listing people along with their date of birth (currently a nvarchar(25))

How can I convert that to a date, and then calculate their age in years?

相关标签:
30条回答
  • 2020-11-22 02:37

    I've done a lot of thinking and searching about this and I have 3 solutions that

    • calculate age correctly
    • are short (mostly)
    • are (mostly) very understandable.

    Here are testing values:

    DECLARE @NOW DATETIME = '2013-07-04 23:59:59' 
    DECLARE @DOB DATETIME = '1986-07-05' 
    

    Solution 1: I found this approach in one js library. It's my favourite.

    DATEDIFF(YY, @DOB, @NOW) - 
      CASE WHEN DATEADD(YY, DATEDIFF(YY, @DOB, @NOW), @DOB) > @NOW THEN 1 ELSE 0 END
    

    It's actually adding difference in years to DOB and if it is bigger than current date then subtracts one year. Simple right? The only thing is that difference in years is duplicated here.

    But if you don't need to use it inline you can write it like this:

    DECLARE @AGE INT = DATEDIFF(YY, @DOB, @NOW)
    IF DATEADD(YY, @AGE, @DOB) > @NOW
    SET @AGE = @AGE - 1
    

    Solution 2: This one I originally copied from @bacon-bits. It's the easiest to understand but a bit long.

    DATEDIFF(YY, @DOB, @NOW) - 
      CASE WHEN MONTH(@DOB) > MONTH(@NOW) 
        OR MONTH(@DOB) = MONTH(@NOW) AND DAY(@DOB) > DAY(@NOW) 
      THEN 1 ELSE 0 END
    

    It's basically calculating age as we humans do.


    Solution 3: My friend refactored it into this:

    DATEDIFF(YY, @DOB, @NOW) - 
      CEILING(0.5 * SIGN((MONTH(@DOB) - MONTH(@NOW)) * 50 + DAY(@DOB) - DAY(@NOW)))
    

    This one is the shortest but it's most difficult to understand. 50 is just a weight so the day difference is only important when months are the same. SIGN function is for transforming whatever value it gets to -1, 0 or 1. CEILING(0.5 * is the same as Math.max(0, value) but there is no such thing in SQL.

    0 讨论(0)
  • 2020-11-22 02:38
    SELECT ID,
    Name,
    DATEDIFF(yy,CONVERT(DATETIME, DOB),GETDATE()) AS AGE,
    DOB
    FROM MyTable
    
    0 讨论(0)
  • 2020-11-22 02:39

    EDIT: THIS ANSWER IS INCORRECT. I leave it in here as a warning to anyone tempted to use dayofyear, with a further edit at the end.


    If, like me, you do not want to divide by fractional days or risk rounding/leap year errors, I applaud @Bacon Bits comment in a post above https://stackoverflow.com/a/1572257/489865 where he says:

    If we're talking about human ages, you should calculate it the way humans calculate age. It has nothing to do with how fast the earth moves and everything to do with the calendar. Every time the same month and day elapses as the date of birth, you increment age by 1. This means the following is the most accurate because it mirrors what humans mean when they say "age".

    He then offers:

    DATEDIFF(yy, @date, GETDATE()) -
    CASE WHEN (MONTH(@date) > MONTH(GETDATE())) OR (MONTH(@date) = MONTH(GETDATE()) AND DAY(@date) > DAY(GETDATE()))
    THEN 1 ELSE 0 END
    

    There are several suggestions here involving comparing the month & day (and some get it wrong, failing to allow for the OR as correctly here!). But nobody has offered dayofyear, which seems so simple and much shorter. I offer:

    DATEDIFF(year, @date, GETDATE()) -
    CASE WHEN DATEPART(dayofyear, @date) > DATEPART(dayofyear, GETDATE()) THEN 1 ELSE 0 END
    

    [Note: Nowhere in SQL BOL/MSDN is what DATEPART(dayofyear, ...) returns actually documented! I understand it to be a number in the range 1--366; most importantly, it does not change by locale as per DATEPART(weekday, ...) & SET DATEFIRST.]


    EDIT: Why dayofyear goes wrong: As user @AeroX has commented, if the birth/start date is after February in a non leap year, the age is incremented one day early when the current/end date is a leap year, e.g. '2015-05-26', '2016-05-25' gives an age of 1 when it should still be 0. Comparing the dayofyear in different years is clearly dangerous. So using MONTH() and DAY() is necessary after all.

    0 讨论(0)
  • 2020-11-22 02:39

    Since there isn't one simple answer that always gives the correct age, here's what I came up with.

    SELECT DATEDIFF(YY, DateOfBirth, GETDATE()) - 
         CASE WHEN RIGHT(CONVERT(VARCHAR(6), GETDATE(), 12), 4) >= 
                   RIGHT(CONVERT(VARCHAR(6), DateOfBirth, 12), 4) 
         THEN 0 ELSE 1 END AS AGE 
    

    This gets the year difference between the birth date and the current date. Then it subtracts a year if the birthdate hasn't passed yet.

    Accurate all the time - regardless of leap years or how close to the birthdate.

    Best of all - no function.

    0 讨论(0)
  • 2020-11-22 02:41

    Gotta throw this one out there. If you convert the date using the 112 style (yyyymmdd) to a number you can use a calculation like this...

    (yyyyMMdd - yyyyMMdd) / 10000 = difference in full years

    declare @as_of datetime, @bday datetime;
    select @as_of = '2009/10/15', @bday = '1980/4/20'
    
    select 
        Convert(Char(8),@as_of,112),
        Convert(Char(8),@bday,112),
        0 + Convert(Char(8),@as_of,112) - Convert(Char(8),@bday,112), 
        (0 + Convert(Char(8),@as_of,112) - Convert(Char(8),@bday,112)) / 10000
    

    output

    20091015    19800420    290595  29
    
    0 讨论(0)
  • 2020-11-22 02:42
    CASE WHEN datepart(MM, getdate()) < datepart(MM, BIRTHDATE) THEN ((datepart(YYYY, getdate()) - datepart(YYYY, BIRTH_DATE)) -1 )
         ELSE 
            CASE WHEN datepart(MM, getdate()) = datepart(MM, BIRTHDATE)
                THEN 
                    CASE WHEN datepart(DD, getdate()) < datepart(DD, BIRTHDATE) THEN ((datepart(YYYY, getdate()) - datepart(YYYY, BIRTHDATE)) -1 )
                        ELSE (datepart(YYYY, getdate()) - datepart(YYYY, BIRTHDATE))
                    END
            ELSE (datepart(YYYY, getdate()) - datepart(YYYY, BIRTHDATE)) END            
        END
    
    0 讨论(0)
提交回复
热议问题