Как рассчитать возраст (в годах) на основе даты рождения и getDate ()


172

У меня есть таблица со списком людей и датой их рождения (в настоящее время 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

16
Почему вы храните значения даты в виде строк, используя nvarchar (25) вместо использования собственной даты базы данных или типа datetime?
Jesper

Вопрос помечен как 2005, а не 2008, так что собственный тип «Дата» недоступен, но определенно дата и время, и это можно утверждать SmallDateTime, поскольку вам не нужна точность.
Андрей

Привет, причина сохранения дат в виде varchar заключается в том, что я импортирую их из схемы сервера, отличной от SQL, были некоторые проблемы с импортом их как datetime (и других форматов дат) и преобразование varchar в норму
Jimmy

7
@ James.Elsey, у вас возникли проблемы с импортом, и в результате все даты действительны? никогда не может быть уверен, если вы не используете datetime или smalldatetime, с varchar вы можете заставить работать ваш импорт, но у вас возникнут другие проблемы. Кроме того, я никогда не буду хранить возраст, он меняется каждый день, используйте View
KM.

@KM Да, была проблема с импортом этих данных как даты, единственное жизнеспособное решение на тот момент заключалось в том, чтобы импортировать их как nvarchars. Этот выбор будет частью ночной работы, поэтому сохранение возраста не должно быть проблемой
Джимми

Ответы:


256

Есть проблемы с високосным годом / днями и следующим методом, см. Обновление ниже:

попробуй это:

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 и получить десятичные, но это не будет столь же точным , как метод ниже.

Лучший метод для лет в десятичном

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

24
Это тоже не точное решение. Если я выберу свой @dob '1986-07-05 00:00:00', и я выполню это (используйте другую переменную вместо GETDATE()) в '2013-07-04 23:59:59', это говорит, что я мне 27, а в тот момент я еще не Пример кода: declare @startDate nvarchar(100) = '1986-07-05 00:00:00' declare @endDate nvarchar(100) = '2013-07-04 23:59:59' SELECT DATEDIFF(hour,@startDate,@endDate)/8766.0 AS AgeYearsDecimal ,CONVERT(int,ROUND(DATEDIFF(hour,@startDate,@endDate)/8766.0,0)) AS AgeYearsIntRound ,DATEDIFF(hour,@startDate,@endDate)/8766 AS AgeYearsIntTrunc
bartlaarhoven

20
Это не является точным, поскольку предполагает 8766 часов в год, что составляет 365,25 дней. Поскольку нет лет с 365,25 днями, это будет неверно ближе к дате рождения человека чаще, чем это правильно. Этот метод все еще будет более точным.
Беконные биты

1
Второй комментарий @Bacon Bits - это часто будет неправильно, если текущая дата близка к дате рождения человека.
вспышка

2
Я думаю, что первый блок текста делает этот ответ запутанным. Если у вашего обновленного метода нет проблемы с високосными годами, я бы посоветовал (если вы действительно хотите сохранить его вообще) переместить его в конец вашего ответа.
ajbeaven

1
Если вы хотите ТОЧНЫЙ расчет, то DATEDIFF не будет делать это @ShailendraMishra, потому что он приближается к дням и т. Д. Например, select datediff(year, '2000-01-05', '2018-01-04')возвращает 18, а не 17, как должно. Я использовал пример выше под заголовком «Лучший метод для лет в INT», и он отлично работает. Спасибо!
openwonk

133

Должен выбросить это там. Если вы конвертируете дату, используя стиль 112 (ггггммдд), в число, вы можете использовать такой расчет ...

(yyyyMMdd - 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

14
Это почти волшебно в том, как это решает все проблемы високосного года. Возможно, стоит отметить, что 112 - это специальное число для функции CONVERT, которая форматирует дату как ггггммдд. Возможно, это не очевидно для всех, когда вы смотрите на это.
Дерек Томес

5
Ты гений!
ercan

Моя команда столкнулась с проблемой, когда дата, которую мы использовали для определения возраста, совпадала с датой, с которой мы сравниваем ее. Мы заметили, что когда они будут в один и тот же день (и если возраст будет нечетным), возраст будет на единицу меньше. Это сработало отлично!
Шик Geek

1
Это правильный ответ - пожалуйста, отметьте его как таковой.
Snympi

4
Простейший / самый короткий код для этого точно такого же метода вычисления в SQL Server 2012+code: SELECT [Age] = (0+ FORMAT(@as_of,'yyyyMMdd') - FORMAT(@bday,'yyyyMMdd') ) /10000 --The 0+ part tells SQL to calc the char(8) as numbers
ukgav

44

Я использовал этот запрос в нашем производственном коде почти 10 лет:

SELECT FLOOR((CAST (GetDate() AS INTEGER) - CAST(Date_of_birth AS INTEGER)) / 365.25) AS Age

6
Это не плохо, но не на 100%, 2007/10/16 сообщит о возрасте 2 на 2009/10/15
Эндрю

4
Дох, мы упускаем очевидное, это после полудня, getdate возвращает int, поэтому, конечно, будет округляться. Я скопировал, вставил твой ответ и запустил его, поэтому автоматически использовал getdate, а не литерал.
Андрей

2
Элегантно и просто. Спасибо!
Уильям М.Б.

9
Если мы говорим о человеческом возрасте, вы должны рассчитать, как люди вычисляют возраст. Это не имеет ничего общего с тем, как быстро движется земля, и не имеет отношения к календарю. Каждый раз, когда тот же месяц и день истекают в качестве даты рождения, вы увеличиваете возраст на 1. Это означает, что следующее является наиболее точным, поскольку оно отражает то, что люди имеют в виду, когда говорят «возраст»:DATEDIFF(yy, @BirthDate, GETDATE()) - CASE WHEN (MONTH(@BirthDate) >= MONTH(GETDATE())) AND DAY(@BirthDate) > DAY(GETDATE()) THEN 1 ELSE 0 END
Биты бекона

5
Извините, этот синтаксис неправильный. CASE WHEN (MONTH(@date) > MONTH(GETDATE())) OR (MONTH(@date) = MONTH(GETDATE()) AND DAY(@date) > DAY(GETDATE())) THEN 1 ELSE 0 END
Бекон Биты

31

Многие из приведенных выше решений неверны. 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

Почему ты умножаешь на 100? Это работает для меня, так как я пытаюсь воспроизвести в базе данных то, что существует в нашей библиотеке кода - но я не смог объяснить вашу функцию. Это может быть глупый вопрос :)
Джен

6
Спасибо! Именно код, который я ожидал здесь. Это единственный правильный код в этой теме без (безобразных) строковых преобразований! @Jen Он берет месяц и день DoB (например, 25 сентября) и превращает его в целочисленное значение 0925(или 925). Он делает то же самое с текущей датой (как 16 декабря 1216) и затем проверяет, прошло ли уже целое значение DoB. Чтобы создать это целое число, месяц нужно умножить на 100.
bartlaarhoven

Спасибо @bartlaarhoven :)
Джен

Я просто упомяну, что, хотя это позволяет избежать преобразования строк, вместо этого выполняется много приведений. Мое тестирование показывает, что оно не намного быстрее, чем ответ dotjoe , а код более подробный.
StriplingWarrior

Принятый ответ имеет гораздо более простой ответ INT:(CONVERT(int,CONVERT(char(8),@Now,112))-CONVERT(char(8),@Dob,112))/10000
КМ.

19

Вам нужно подумать о том, как обходится команда 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>

Который я адаптировал отсюда .

Обратите внимание, что 28 февраля будет считаться днем ​​рождения скачка для не високосных лет, например, человек, родившийся 29 февраля 2020 года, будет считаться годовалым 28 февраля 2021 года вместо 01 марта 2021 года.


@ Эндрю - Исправлено - Я пропустил одну из замен
Эд Харпер

1
Упрощенная версияSELECT DATEDIFF(year, DOB, getdate()) + CASE WHEN (DATEADD(year,DATEDIFF(year, DOB, getdate()) , DOB) > getdate()) THEN - 1 ELSE 0 END)
Питер

Это правильный подход; Я не понимаю, почему за хаки так много голосуют.
Салман А

8

РЕДАКТИРОВАТЬ: ЭТО ОТВЕТ НЕПРАВИЛЬНЫЙ. Я оставляю это здесь как предупреждение для всех, кто испытывает желание использовать 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.]


РЕДАКТИРОВАТЬ: Почему dayofyearидет не так : как прокомментировал пользователь @AeroX, если дата рождения / начала - после февраля в не високосный год, возраст увеличивается на один день раньше, когда текущая / конечная дата является високосным, например '2015-05-26', '2016-05-25'дает возраст 1, когда он все еще должен быть 0. Сравнение dayofyearв разные годы явно опасно. Так что использовать MONTH()и DAY()надо ведь.


За это следует проголосовать или даже отметить как ответ. Это коротко, изящно и логически правильно.
z00l

1
Для всех, кто родился после февраля, их возраст увеличивается на один день раньше на каждый високосный год с использованием DayOfYearметода.
AeroX

4
@AeroX Спасибо, что обнаружили этот недостаток. Я решил оставить свое решение в качестве предупреждения для тех, кто мог или использовал dayofyear, но четко отредактировал, чтобы показать, почему это идет не так. Я надеюсь, что это подходит.
JonBrave

5

Поскольку не существует одного простого ответа, который всегда дает правильный возраст, вот что я придумал.

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 

Получается разница в году между датой рождения и текущей датой. Затем вычитается год, если дата рождения еще не прошла.

Точно все время - независимо от високосных лет или как близко к дате рождения.

Лучше всего - без функции.


3
SELECT ID,
Name,
DATEDIFF(yy,CONVERT(DATETIME, DOB),GETDATE()) AS AGE,
DOB
FROM MyTable

1
Вы хотите, чтобы getdate был вторым аргументом, а не первым, иначе вы получите отрицательные результаты чисел и округления datediff, поэтому выберите datediff (yy, '20081231', getdate ()), чтобы сообщить возраст 1, но им будет всего 10 месяцев. ,
Андрей

1
Этот расчет дает неверные расчеты для людей, у которых еще не было дня рождения в этом году.

3

Что о:

DECLARE @DOB datetime
SET @DOB='19851125'   
SELECT Datepart(yy,convert(date,GETDATE())-@DOB)-1900

Разве это не позволило бы избежать всех этих проблем округления, усечения и удаления?


Ваш расчет не является точным. Например: сбой, если вы берете '1986-07-05 00:00:00'за DOB и '2013-07-04 23:59:59'за текущее время.
drinovc

@ub_coding Вы предлагаете и отвечаете или задаете другой вопрос?
Аарон С

this: DECLARE @DOB datetime SET @ DOB = '19760229' SELECT Datepart (yy, convert (datetime, '19770228') - @ DOB) -1900 = 1 основная проблема в большинстве решений - пробел 29, это объясняет усечение округления и т. д. .
Леонардо Маркиза де Соуза

3

Я полагаю, что это похоже на другие опубликованные здесь .... но это решение работало для примеров високосного года с 29.02.1976 по 03.01.2011, а также работало для случая в течение первого года .. как 07/04 С 2011 по 07/03/2012, которые последний раз публиковал о решении для високосного года, не подходил для первого варианта использования.

SELECT FLOOR(DATEDIFF(DAY, @date1 , @date2) / 365.25)

Нашел здесь .


3

Просто проверьте, возможен ли приведенный ниже ответ.

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        
 )

2
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/


Более короткий способ сделать это в SQL Server 2012+ заключается в следующем, и он позволяет избежать преобразования в 112, и слово не требуется:code: SELECT [Age] = (0+ FORMAT(@ToDate,'yyyyMMdd') - FORMAT(@DOB,'yyyyMMdd') ) /10000
ukgav

2

Я много думал и искал об этом, и у меня есть 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

Это фактически добавляет разницу в годах к DOB, и если она больше текущей даты, то вычитается один год. Просто верно? Единственное, что разница в годах здесь дублируется.

Но если вам не нужно использовать его в строке, вы можете написать это так:

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 такого нет.


1
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

1
select floor((datediff(day,0,@today) - datediff(day,0,@birthdate)) / 365.2425) as age

Здесь 365.25 ответов. Помните, как определяются високосные годы:

  • Каждые четыре года
    • кроме каждых 100 лет
      • кроме каждые 400 лет

Отличный ответ. Для тех, кому интересно, вот объяснение того, почему 365.2425 - правильное значение: grc.nasa.gov/WWW/k-12/Numbers/Math/Maturgical_Thinking/…
vvvv4d

0

Как насчет этого:

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

0

Попробуй это

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'

0

Попробуйте это решение:

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

0

Это правильно решит проблемы с днем ​​рождения и округлением:

DECLARE @dob  datetime
SET @dob='1992-01-09 00:00:00'

SELECT DATEDIFF(YEAR, '0:0', getdate()-@dob)

2
Не правильно обрабатывает високосные годы. SELECT DATEDIFF (YEAR, '0: 0', convert (datetime, '2014-02-28') -'2012-02-29 ') дает 2, но должно быть только 1
Питер Керр

0

Решение Эда Харпера - самое простое из найденных мной, которое никогда не возвращает неправильный ответ, когда месяц и день двух дат разделены 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

0

Ответ, помеченный как правильный, ближе к точности, но не соответствует следующему сценарию: где Год рождения - високосный, а день - после февраля

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 февраля и т. Д.

Пожалуйста, поправьте меня, если в этой формуле есть лазейки.


Окончательная формула почти такая же, как в этом ответе stackoverflow.com/a/1572235/168747 . Но тот, что здесь менее читабелен и содержит ненужные floor.
Марек

0
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

0

Вот как я могу рассчитать возраст, учитывая дату рождения и текущую дату.

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

0
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

-1: будет неправильно в любое время, month(compare) > month(dob)НО day(compare) < day(dob), например select dbo.AgeAtDate('2000-01-14', '2016-02-12').
JonBrave

Вы правы, спасибо за это дело, обновили функцию
Вова

0
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

0

Как насчет решения с только функциями даты, а не математики, не заботится о високосном году

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

0

После попытки многих методов, это работает 100% времени, используя современную функцию MS SQL FORMAT вместо преобразования в стиль 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

-2

Мы использовали что-то вроде здесь, но затем взяли средний возраст:

ROUND(avg(CONVERT(int,DATEDIFF(hour,DOB,GETDATE())/8766.0)),0) AS AverageAge

Обратите внимание, КРУГЛЫЙ снаружи, а не внутри. Это позволит AVG быть более точным, а мы КРУГЛЫМ только один раз. Делая это быстрее тоже.



-2
SELECT CAST(DATEDIFF(dy, @DOB, GETDATE()+1)/365.25 AS int)

Я вижу вниз голоса, но я не вижу примеров, которые доказывают, что это не работает.
Алекс
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.