Как рассчитать возраст (в годах) на основе даты рождения и getDate()
у меня есть таблица с перечислением людей вместе с их датой рождения(в настоящее время nvarchar (25))
Как я могу преобразовать это в дату, а затем вычислить их возраст в годах?
мои данные выглядят следующим образом
ID Name DOB
1 John 1992-01-09 00:00:00
2 Sally 1959-05-20 00:00:00
Я хотел бы видеть:
ID Name AGE DOB
1 John 17 1992-01-09 00:00:00
2 Sally 50 1959-05-20 00:00:00
30 ответов:
есть проблемы с високосного года / дней и следующий метод, см. обновление ниже:
попробуйте это:
DECLARE @dob datetime SET @dob='1992-01-09 00:00:00' SELECT DATEDIFF(hour,@dob,GETDATE())/8766.0 AS AgeYearsDecimal ,CONVERT(int,ROUND(DATEDIFF(hour,@dob,GETDATE())/8766.0,0)) AS AgeYearsIntRound ,DATEDIFF(hour,@dob,GETDATE())/8766 AS AgeYearsIntTruncвыход:
AgeYearsDecimal AgeYearsIntRound AgeYearsIntTrunc --------------------------------------- ---------------- ---------------- 17.767054 18 17 (1 row(s) affected)обновление вот несколько более точных методов:
ЛУЧШИЙ МЕТОД В ТЕЧЕНИЕ МНОГИХ ЛЕТ В INT
DECLARE @Now datetime, @Dob datetime SELECT @Now='1990-05-05', @Dob='1980-05-05' --results in 10 --SELECT @Now='1990-05-04', @Dob='1980-05-05' --results in 9 --SELECT @Now='1989-05-06', @Dob='1980-05-05' --results in 9 --SELECT @Now='1990-05-06', @Dob='1980-05-05' --results in 10 --SELECT @Now='1990-12-06', @Dob='1980-05-05' --results in 10 --SELECT @Now='1991-05-04', @Dob='1980-05-05' --results in 10 SELECT (CONVERT(int,CONVERT(char(8),@Now,112))-CONVERT(char(8),@Dob,112))/10000 AS AgeIntYearsвы можете изменить выше
10000до10000.0и получить десятичные числа, но это не будет так точно, как метод под.ЛУЧШИЙ МЕТОД В ТЕЧЕНИЕ МНОГИХ ЛЕТ В DECIMAL
DECLARE @Now datetime, @Dob datetime SELECT @Now='1990-05-05', @Dob='1980-05-05' --results in 10.000000000000 --SELECT @Now='1990-05-04', @Dob='1980-05-05' --results in 9.997260273973 --SELECT @Now='1989-05-06', @Dob='1980-05-05' --results in 9.002739726027 --SELECT @Now='1990-05-06', @Dob='1980-05-05' --results in 10.002739726027 --SELECT @Now='1990-12-06', @Dob='1980-05-05' --results in 10.589041095890 --SELECT @Now='1991-05-04', @Dob='1980-05-05' --results in 10.997260273973 SELECT 1.0* DateDiff(yy,@Dob,@Now) +CASE WHEN @Now >= DATEFROMPARTS(DATEPART(yyyy,@Now),DATEPART(m,@Dob),DATEPART(d,@Dob)) THEN --birthday has happened for the @now year, so add some portion onto the year difference ( 1.0 --force automatic conversions from int to decimal * DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,@Now),DATEPART(m,@Dob),DATEPART(d,@Dob)),@Now) --number of days difference between the @Now year birthday and the @Now day / DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,@Now),1,1),DATEFROMPARTS(DATEPART(yyyy,@Now)+1,1,1)) --number of days in the @Now year ) ELSE --birthday has not been reached for the last year, so remove some portion of the year difference -1 --remove this fractional difference onto the age * ( -1.0 --force automatic conversions from int to decimal * DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,@Now),DATEPART(m,@Dob),DATEPART(d,@Dob)),@Now) --number of days difference between the @Now year birthday and the @Now day / DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,@Now),1,1),DATEFROMPARTS(DATEPART(yyyy,@Now)+1,1,1)) --number of days in the @Now year ) END AS AgeYearsDecimal
должен бросить это там. Если вы преобразование дата с использованием стиля 112 (yyyymmdd) для числа вы можете использовать такой расчет...
(ггггммдд - ггггммдд) / 10000 = разница в полных годах
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выход
20091015 19800420 290595 29
я использовал этот запрос в нашем производственном коде почти 10 лет:
SELECT FLOOR((CAST (GetDate() AS INTEGER) - CAST(Date_of_birth AS INTEGER)) / 365.25) AS Age
поэтому многие из вышеперечисленных решений неверны DateDiff (yy,@Dob, @PassedDate) не будут учитывать месяц и день обеих дат. Также принимая части дротика и сравнивая работает только в том случае, если они правильно упорядочены.
СЛЕДУЮЩИЙ КОД РАБОТАЕТ И ОЧЕНЬ ПРОСТ:
create function [dbo].[AgeAtDate]( @DOB datetime, @PassedDate datetime ) returns int with SCHEMABINDING as begin declare @iMonthDayDob int declare @iMonthDayPassedDate int select @iMonthDayDob = CAST(datepart (mm,@DOB) * 100 + datepart (dd,@DOB) AS int) select @iMonthDayPassedDate = CAST(datepart (mm,@PassedDate) * 100 + datepart (dd,@PassedDate) AS int) return DateDiff(yy,@DOB, @PassedDate) - CASE WHEN @iMonthDayDob <= @iMonthDayPassedDate THEN 0 ELSE 1 END End
вам нужно рассмотреть способ выполнения команды datediff.
SELECT CASE WHEN dateadd(year, datediff (year, DOB, getdate()), DOB) > getdate() THEN datediff(year, DOB, getdate()) - 1 ELSE datediff(year, DOB, getdate()) END as Age FROM <table>который я адаптировал из здесь
ИЗМЕНИТЬ: ЭТОТ ОТВЕТ НЕВЕРЕН.я оставляю его здесь в качестве предупреждения для тех, кто соблазн использовать
dayofyear, с дальнейшим редактированием в конце.
если, как и я, вы не хотите делить на дробные дни или рисковать ошибками округления / високосного года, я аплодирую комментарию @Bacon Bits в сообщении выше https://stackoverflow.com/a/1572257/489865 где он говорит:
если мы говорим о возрасте человека, вы должны рассчитайте его так люди вычисляют возраст. Это не имеет ничего общего с тем, как быстро земля движется и все, что связано с календарем. Каждый раз одно и то же месяц и день истекает как дата рождения, вы увеличиваете возраст на 1. Это означает, что следующее является наиболее точным, потому что оно отражает то, что люди имеют в виду, когда они говорят "возраст".
затем он предлагает:
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здесь есть несколько предложений, связанных с сравнением месяца и дня (и некоторые получают его неправильно, не учитывая
ORкак правильно здесь!). Но никто не предложилdayofyear, который кажется таким простым и намного короче. Я предлагаю:DATEDIFF(year, @date, GETDATE()) - CASE WHEN DATEPART(dayofyear, @date) > DATEPART(dayofyear, GETDATE()) THEN 1 ELSE 0 END[Примечание: нигде в SQL BOL / MSDN это то, что
DATEPART(dayofyear, ...)на самом деле возвращает документально! Я понимаю, что это число в диапазоне 1--366; самое главное, это делает не изменить язык какDATEPART(weekday, ...)&SET DATEFIRST.]
EDIT:почему
dayofyearидет не так: как пользователь @AeroX прокомментировал, если дата рождения / начала после февраля в не високосном году, возраст увеличивается на один день раньше, когда текущая / конечная дата является високосным годом, например'2015-05-26','2016-05-25'дает возраст 1, когда он еще должен быть 0. Сравнениеdayofyearв разные годы это явно опасно. Так что с помощьюMONTH()иDAY()надо все-таки.
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это получает разницу в год между датой рождения и текущей датой. Затем он вычитает год, если дата рождения еще не прошла.
точный все время - независимо от того, високосный год или как близко к дате рождения.
лучше всего-нет функции.
о:
DECLARE @DOB datetime SET @DOB='19851125' SELECT Datepart(yy,convert(date,GETDATE())-@DOB)-1900разве это не позволит избежать всех этих округления, усечения и ofsetting вопросов?
Я считаю, что это похоже на другие, размещенные здесь.... но это решение работало для високосного года примеры 02/29/1976 до 03/01/2011, а также работал для случая в течение первого года.. например, 07/04/2011 до 07/03/2012, который последний опубликовал о решении високосного года, не работал для этого случая использования в первый год.
SELECT FLOOR(DATEDIFF(DAY, @date1 , @date2) / 365.25)нашел здесь.
просто проверьте, возможен ли ответ ниже.
DECLARE @BirthDate DATE = '09/06/1979' SELECT ( YEAR(GETDATE()) - YEAR(@BirthDate) - CASE WHEN (MONTH(GETDATE()) * 100) + DATEPART(dd, GETDATE()) > (MONTH(@BirthDate) * 100) + DATEPART(dd, @BirthDate) THEN 1 ELSE 0 END )
DECLARE @DOB datetime set @DOB ='11/25/1985' select floor( ( cast(convert(varchar(8),getdate(),112) as int)- cast(convert(varchar(8),@DOB,112) as int) ) / 10000 )источник: http://beginsql.wordpress.com/2012/04/26/how-to-calculate-age-in-sql-server/
Я много думал и искал об этом, и у меня есть 3 решения, которые
- правильно рассчитать возраст
- короткие (в основном)
- (в основном) очень понятно.
вот тестовые значения:
DECLARE @NOW DATETIME = '2013-07-04 23:59:59' DECLARE @DOB DATETIME = '1986-07-05'
Решение 1: я нашел этот подход в одной библиотеке js. Это мое любимое блюдо.
DATEDIFF(YY, @DOB, @NOW) - CASE WHEN DATEADD(YY, DATEDIFF(YY, @DOB, @NOW), @DOB) > @NOW THEN 1 ELSE 0 ENDэто на самом деле добавление разницы в годах, чтобы сделать и если это больше, чем текущая дата, то вычитает один год. Просто ведь так? Единственное, что здесь дублируется разница в годах.
но если вам не нужно использовать его в строке, вы можете написать его так:
DECLARE @AGE INT = DATEDIFF(YY, @DOB, @NOW) IF DATEADD(YY, @AGE, @DOB) > @NOW SET @AGE = @AGE - 1
решение 2: этот я изначально скопировал из @bacon-bits. Это легче всего понять, но немного долго.
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это в основном расчет возраста, как мы, люди.
решение 3: мой друг преобразовал его в это:
DATEDIFF(YY, @DOB, @NOW) - CEILING(0.5 * SIGN((MONTH(@DOB) - MONTH(@NOW)) * 50 + DAY(@DOB) - DAY(@NOW)))этот самый короткий, но его труднее всего понять.
50это просто вес, поэтому разница в дне важна только тогда, когда месяцы одинаковы.SIGNфункции для преобразования любого значения, оно получает -1, 0 или 1.CEILING(0.5 *это то же самое, чтоMath.max(0, value)но в SQL такого нет.
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
Как насчет этого:
SET @Age = CAST(DATEDIFF(Year, @DOB, @Stamp) as int) IF (CAST(DATEDIFF(DAY, DATEADD(Year, @Age, @DOB), @Stamp) as int) < 0) SET @Age = @Age - 1
Попробуй Такое
DECLARE @date datetime, @tmpdate datetime, @years int, @months int, @days int SELECT @date = '08/16/84' SELECT @tmpdate = @date SELECT @years = DATEDIFF(yy, @tmpdate, GETDATE()) - CASE WHEN (MONTH(@date) > MONTH(GETDATE())) OR (MONTH(@date) = MONTH(GETDATE()) AND DAY(@date) > DAY(GETDATE())) THEN 1 ELSE 0 END SELECT @tmpdate = DATEADD(yy, @years, @tmpdate) SELECT @months = DATEDIFF(m, @tmpdate, GETDATE()) - CASE WHEN DAY(@date) > DAY(GETDATE()) THEN 1 ELSE 0 END SELECT @tmpdate = DATEADD(m, @months, @tmpdate) SELECT @days = DATEDIFF(d, @tmpdate, GETDATE()) SELECT Convert(Varchar(Max),@years)+' Years '+ Convert(Varchar(max),@months) + ' Months '+Convert(Varchar(Max), @days)+'days'
попробуйте это решение:
declare @BirthDate datetime declare @ToDate datetime set @BirthDate = '1/3/1990' set @ToDate = '1/2/2008' select @BirthDate [Date of Birth], @ToDate [ToDate],(case when (DatePart(mm,@ToDate) < Datepart(mm,@BirthDate)) OR (DatePart(m,@ToDate) = Datepart(m,@BirthDate) AND DatePart(dd,@ToDate) < Datepart(dd,@BirthDate)) then (Datepart(yy, @ToDate) - Datepart(yy, @BirthDate) - 1) else (Datepart(yy, @ToDate) - Datepart(yy, @BirthDate))end) Age
Это будет правильно обрабатывать проблемы с Днем рождения и округления:
DECLARE @dob datetime SET @dob='1992-01-09 00:00:00' SELECT DATEDIFF(YEAR, '0:0', getdate()-@dob)
решение Эда Харпера-самое простое, которое я нашел, которое никогда не возвращает неправильный ответ, когда месяц и День двух дат находятся в 1 или менее днях друг от друга. Я сделал небольшую модификацию, чтобы справиться с отрицательным возрастом.
DECLARE @D1 AS DATETIME, @D2 AS DATETIME SET @D2 = '2012-03-01 10:00:02' SET @D1 = '2013-03-01 10:00:01' SELECT DATEDIFF(YEAR, @D1,@D2) + CASE WHEN @D1<@D2 AND DATEADD(YEAR, DATEDIFF(YEAR,@D1, @D2), @D1) > @D2 THEN - 1 WHEN @D1>@D2 AND DATEADD(YEAR, DATEDIFF(YEAR,@D1, @D2), @D1) < @D2 THEN 1 ELSE 0 END AS AGE
ответ, отмеченный как правильный, ближе к точности, но он терпит неудачу в следующем сценарии-где год рождения високосный год и день после февраля месяца
declare @ReportStartDate datetime = CONVERT(datetime, '1/1/2014'), @DateofBirth datetime = CONVERT(datetime, '2/29/1948') FLOOR(DATEDIFF(HOUR,@DateofBirth,@ReportStartDate )/8766)
илиFLOOR(DATEDIFF(HOUR,@DateofBirth,@ReportStartDate )/8765.82) -- Divisor is more accurate than 8766-- следующее решение дает мне более точные результаты.
FLOOR(DATEDIFF(YEAR,@DateofBirth,@ReportStartDate) - (CASE WHEN DATEADD(YY,DATEDIFF(YEAR,@DateofBirth,@ReportStartDate),@DateofBirth) > @ReportStartDate THEN 1 ELSE 0 END ))он работал почти во всех сценариях, учитывая високосный год, дату 29 февраля и т. д.
пожалуйста, поправьте меня, если эта формула имеет какую-либо лазейку.
Declare @dob datetime Declare @today datetime Set @dob = '05/20/2000' set @today = getdate() select CASE WHEN dateadd(year, datediff (year, @dob, @today), @dob) > @today THEN datediff (year, @dob, @today) - 1 ELSE datediff (year, @dob, @today) END as Age
вот как я вычисляю возраст с учетом даты рождения и текущей даты.
select case when cast(getdate() as date) = cast(dateadd(year, (datediff(year, '1996-09-09', getdate())), '1996-09-09') as date) then dateDiff(yyyy,'1996-09-09',dateadd(year, 0, getdate())) else dateDiff(yyyy,'1996-09-09',dateadd(year, -1, getdate())) end as MemberAge go
CREATE function dbo.AgeAtDate( @DOB datetime, @CompareDate datetime ) returns INT as begin return CASE WHEN @DOB is null THEN null ELSE DateDiff(yy,@DOB, @CompareDate) - CASE WHEN datepart(mm,@CompareDate) > datepart(mm,@DOB) OR (datepart(mm,@CompareDate) = datepart(mm,@DOB) AND datepart(dd,@CompareDate) >= datepart(dd,@DOB)) THEN 0 ELSE 1 END END End GO
DECLARE @FromDate DATETIME = '1992-01-2623:59:59.000', @ToDate DATETIME = '2016-08-10 00:00:00.000', @Years INT, @Months INT, @Days INT, @tmpFromDate DATETIME SET @Years = DATEDIFF(YEAR, @FromDate, @ToDate) - (CASE WHEN DATEADD(YEAR, DATEDIFF(YEAR, @FromDate, @ToDate), @FromDate) > @ToDate THEN 1 ELSE 0 END) SET @tmpFromDate = DATEADD(YEAR, @Years , @FromDate) SET @Months = DATEDIFF(MONTH, @tmpFromDate, @ToDate) - (CASE WHEN DATEADD(MONTH,DATEDIFF(MONTH, @tmpFromDate, @ToDate), @tmpFromDate) > @ToDate THEN 1 ELSE 0 END) SET @tmpFromDate = DATEADD(MONTH, @Months , @tmpFromDate) SET @Days = DATEDIFF(DAY, @tmpFromDate, @ToDate) - (CASE WHEN DATEADD(DAY, DATEDIFF(DAY, @tmpFromDate, @ToDate), @tmpFromDate) > @ToDate THEN 1 ELSE 0 END) SELECT @FromDate FromDate, @ToDate ToDate, @Years Years, @Months Months, @Days Days
Как насчет решения только с функциями даты, а не математикой, не беспокоится о високосном годе
CREATE FUNCTION dbo.getAge(@dt datetime) RETURNS int AS BEGIN RETURN DATEDIFF(yy, @dt, getdate()) - CASE WHEN MONTH(@dt) > MONTH(GETDATE()) OR (MONTH(@dt) = MONTH(GETDATE()) AND DAY(@dt) > DAY(GETDATE())) THEN 1 ELSE 0 END END
после попытки многих методов, это работает 100% времени, используя современную функцию формата MS SQL вместо преобразования в стиль 112. Либо будет работать, но это наименьший код.
может ли кто-нибудь найти комбинацию дат, которая не работает? Я не думаю, что есть один :)
--Set parameters, or choose from table.column instead: DECLARE @DOB DATE = '2000/02/29' -- If @DOB is a leap day... ,@ToDate DATE = '2018/03/01' --...there birthday in this calculation will be --0+ part tells SQL to calc the char(8) as numbers: SELECT [Age] = (0+ FORMAT(@ToDate,'yyyyMMdd') - FORMAT(@DOB,'yyyyMMdd') ) /10000
мы использовали что-то вроде здесь, но затем взяли средний возраст:
ROUND(avg(CONVERT(int,DATEDIFF(hour,DOB,GETDATE())/8766.0)),0) AS AverageAgeобратите внимание, что раунд находится снаружи, а не внутри. Это позволит AVG быть более точным, и мы округляем только один раз. Что делает его быстрее тоже.
select DATEDIFF(yy,@DATE,GETDATE()) - case when DATEPART(mm,GETDATE())*100+DATEPART(dd,GETDATE())>= DATEPART(mm,@DATE)*100+DATEPART(dd,@DATE) THEN 0 ELSE 1 END
select datediff(day,'1991-03-16',getdate()) \for days,get date refers today date select datediff(year,'1991-03-16',getdate()) \for years select datediff(month,'1991-03-16',getdate()) \for month
Comments