Como calcular a idade (em anos) com base na Data de nascimento e getDate ()

171

Eu tenho uma tabela listando as pessoas junto com a data de nascimento (atualmente uma nvarchar (25))

Como posso converter isso para uma data e calcular sua idade em anos?

Meus dados são os seguintes

ID    Name   DOB
1     John   1992-01-09 00:00:00
2     Sally  1959-05-20 00:00:00

Eu gostaria de ver:

ID    Name   AGE  DOB
1     John   17   1992-01-09 00:00:00
2     Sally  50   1959-05-20 00:00:00
Jimmy
fonte
16
Por que você está armazenando valores de data como seqüências de caracteres usando nvarchar (25) em vez de usar a data nativa do banco de dados ou o tipo de data e hora?
Jesper
A pergunta é etiquetada 2005 e não 2008, portanto o tipo nativo de 'Data' não está disponível, mas definitivamente uma data e hora, e pode-se argumentar SmallDateTime, já que você não precisa da precisão.
Andrew
Oi, o motivo para manter as datas como varchar é porque eu estou importando isso de um esquema de servidor não-SQL, houve alguns problemas ao importá-las como data e hora (e os outros formatos de data) e varchar convertido ok
Jimmy
7
@ James.Elsey, você teve problemas para importar e, como resultado, todas as datas são válidas? você nunca pode ter certeza, a menos que você use um datetime ou smalldatetime, com varchar, poderá fazer com que sua importação funcione, mas tenha outros problemas na linha. Além disso, eu nunca armazenaria a idade, ela muda a cada dia, use um View
KM.
@KM Sim, houve um problema ao importar esses dados como uma data, a única solução viável na época era importá-los como nvarchars. Este seleto vai ser parte de um trabalho noturno assim armazenar a idade não deve ser um problema
Jimmy

Respostas:

256

Há problemas com o ano bissexto / dia e o método a seguir, consulte a atualização abaixo:

tente isto:

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

RESULTADO:

AgeYearsDecimal                         AgeYearsIntRound AgeYearsIntTrunc
--------------------------------------- ---------------- ----------------
17.767054                               18               17

(1 row(s) affected)

ATUALIZAÇÃO aqui estão alguns métodos mais precisos:

MELHOR MÉTODO DE ANOS NO 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

você pode alterar o acima 10000para 10000.0e obter decimais, mas não vai ser tão preciso quanto o método abaixo.

MELHOR MÉTODO DE ANOS EM 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
KM.
fonte
24
Esta também não é uma solução exata. Se eu pegar meu próprio @dob como '1986-07-05 00:00:00' e eu o executaria (use outra variável em vez de GETDATE()) em '2013-07-04 23:59:59', ele diz que eu tenho 27 anos, enquanto naquele momento ainda não estou. Código de exemplo: 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
Isso não é preciso, pois assume 8766 horas por ano, o que resulta em 365,25 dias. Como não há anos com 365,25 dias, isso estará incorreto perto da data de nascimento da pessoa com mais frequência do que a correta. Este método ainda será mais preciso.
Bacon Bits
1
Segundo comentário do @Bacon Bits - isso geralmente estará errado quando a data atual estiver próxima da data de nascimento de uma pessoa.
flash
2
Eu acho que o primeiro bloco de texto torna essa resposta confusa. Se o seu método atualizado não tiver o problema de anos bissextos, sugiro (se você realmente quiser mantê-lo) que o mova para baixo na parte inferior da sua resposta.
precisa saber é
1
Se você quiser um EXATO cálculo, então DATEDIFF não vai fazê-lo @ShailendraMishra, porque aproxima dias, etc. Por exemplo, select datediff(year, '2000-01-05', '2018-01-04')retorna 18, não 17 como deveria. Usei o exemplo acima no título "MELHOR MÉTODO DE ANOS INT" e funciona perfeitamente. Obrigado!
Openwonk
132

Tenho que jogar esse aqui fora. Se você converter a data usando o estilo 112 (aaaammdd) em um número, poderá usar um cálculo como este ...

(aaaaMMdd - aaaaMMdd) / 10000 = diferença em anos completos

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

resultado

20091015    19800420    290595  29
dotjoe
fonte
14
Isso é quase mágico na maneira como resolve todos os problemas do ano bissexto. Pode-se notar que 112 é um número especial para a função CONVERT que formata a data como aaaammdd. Possivelmente não é óbvio para todos quando você olha para ele.
Derek Tomes 27/05
5
Você é um gênio!
ercan
Minha equipe estava enfrentando um problema quando a data em que estávamos usando para encontrar a idade era no mesmo dia da data em que a comparamos. Estávamos percebendo que quando eles fossem no mesmo dia (e se a idade fosse estranha), a idade seria reduzida em um. Isso funcionou perfeitamente!
The Sheek Geek
1
Esta é a resposta correta - marque-a como tal.
Snympi 28/07/19
4
O código mais simples / mais curto para este mesmo método de cálculo exato no 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

Eu tenho usado essa consulta em nosso código de produção há quase 10 anos:

SELECT FLOOR((CAST (GetDate() AS INTEGER) - CAST(Date_of_birth AS INTEGER)) / 365.25) AS Age
J__
fonte
6
Não é ruim, mas não é 100%, 16/10/2007 relatará uma idade de 2 anos em 15/10/2009
Andrew
4
Doh, estamos perdendo o óbvio, é depois do meio dia, getdate retorna um int, então será arredondado, é claro. Eu copiei colei sua resposta e a executei, então usei getdate automaticamente, não o literal.
22415 Andrew
2
Elegante e simples. Obrigado!
William MB
9
Se estamos falando de idades humanas, você deve calcular da mesma forma que os seres humanos calculam a idade. Não tem nada a ver com a rapidez com que a Terra se move e tudo a ver com o calendário. Toda vez que o mesmo mês e dia decorre como a data de nascimento, você aumenta a idade em 1. Isso significa que o seguinte é o mais preciso, porque reflete o que os humanos querem dizer quando dizem "idade":DATEDIFF(yy, @BirthDate, GETDATE()) - CASE WHEN (MONTH(@BirthDate) >= MONTH(GETDATE())) AND DAY(@BirthDate) > DAY(GETDATE()) THEN 1 ELSE 0 END
Bits de bacon
5
Desculpe, essa sintaxe está errada. CASE WHEN (MONTH(@date) > MONTH(GETDATE())) OR (MONTH(@date) = MONTH(GETDATE()) AND DAY(@date) > DAY(GETDATE())) THEN 1 ELSE 0 END
Bacon Bits
31

Muitas das soluções acima estão erradas. O DateDiff (aa, @ Dob, @PassedDate) não considerará o mês e o dia das duas datas. Também pegar as peças do dardo e comparar só funciona se elas forem encomendadas corretamente.

O SEGUINTE CÓDIGO FUNCIONA E É MUITO SIMPLES:

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
user2634514
fonte
Por que você está multiplicando por 100? Isso funciona para mim, pois estou tentando replicar no banco de dados o que existe em nossa biblioteca de códigos - mas não consegui explicar sua função. Isso pode ser uma pergunta estúpida :)
Jen
6
Obrigado! Exatamente o código que eu estava esperando aqui. Este é o único código exatamente correto neste encadeamento sem transformações de string (feias)! @Jen Leva o mês e o dia do DoB (como 25 de setembro) e o transforma em um valor inteiro 0925(ou 925). Ele faz o mesmo com a data atual (como ocorre em 16 de dezembro 1216) e depois verifica se o valor inteiro DoB já passou. Para criar este número inteiro, o mês deve ser multiplicado por 100.
bartlaarhoven
Obrigado @bartlaarhoven :)
Jen
Mencionarei apenas que, embora isso evite transformações de strings, ele faz muita conversão. Meus testes mostram que não é significativamente mais rápido que a resposta de dotjoe , e o código é mais detalhado.
usar o seguinte
a resposta aceita tem uma resposta INT muito mais simples:(CONVERT(int,CONVERT(char(8),@Now,112))-CONVERT(char(8),@Dob,112))/10000
KM.
19

Você precisa considerar a maneira como o comando datatediff arredonda.

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>

Que eu adaptei daqui .

Observe que o dia 28 de fevereiro será considerado o aniversário de um salto para anos não bissextos, por exemplo, uma pessoa nascida em 29 de fevereiro de 2020 será considerada com 1 ano em 28 de fevereiro de 2021 em vez de 01 de março de 2021.

Ed Harper
fonte
@ Andrew - Corrigido - Eu perdi uma das substituições
Ed Harper
1
Versão simplificadaSELECT DATEDIFF(year, DOB, getdate()) + CASE WHEN (DATEADD(year,DATEDIFF(year, DOB, getdate()) , DOB) > getdate()) THEN - 1 ELSE 0 END)
Peter
Essa é a abordagem correta; Não entendo por que os hacks são tão votados.
Salman A
8

EDIT: ESTA RESPOSTA É INCORRETA. Deixo aqui como um aviso para qualquer pessoa tentada a usar dayofyear, com uma edição adicional no final.


Se, como eu, você não quiser dividir por dias fracionários ou arriscar erros de arredondamento / ano bissexto, eu aplaudo o comentário do @Bacon Bits em uma postagem acima https://stackoverflow.com/a/1572257/489865, onde ele diz:

Se estamos falando de idades humanas, você deve calcular da mesma forma que os humanos calculam a idade. Não tem nada a ver com a rapidez com que a Terra se move e tudo a ver com o calendário. Toda vez que o mesmo mês e dia decorre como a data de nascimento, você aumenta a idade em 1. Isso significa que o seguinte é o mais preciso, porque reflete o que os humanos querem dizer quando dizem "idade".

Ele então oferece:

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

Existem várias sugestões aqui que envolvem comparar mês e dia (e algumas erram, deixando de permitir o ORcorreto aqui!). Mas ninguém ofereceu dayofyear, o que parece tão simples e muito mais curto. Eu ofereço:

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

[Nota: Em nenhum lugar no SQL BOL / MSDN está o que de DATEPART(dayofyear, ...)fato está documentado! Entendo que seja um número no intervalo de 1 a 366; o mais importante é que não muda por localidade conforme DATEPART(weekday, ...)& SET DATEFIRST.]


EDIT: Por que dayofyeardá errado : Como usuário @AeroX comentou, se o nascimento / data de início é depois de fevereiro em um ano não bissexto, a idade é incrementado um dia antes, quando a data / end atual é um ano bissexto, por exemplo '2015-05-26', '2016-05-25'dá uma 1 ano de idade, quando ainda deveria ser 0. Comparar os dayofyearanos diferentes é claramente perigoso. Então, usando MONTH()e DAY()é necessário, afinal.

JonBrave
fonte
Isso deve ser votado ou até marcado como a resposta. É curto, elegante e logicamente correto.
Z00l 23/03/16
1
Para todos os nascidos após fevereiro, sua idade é incrementada um dia antes de cada ano bissexto, usando o DayOfYearmétodo
AeroX 23/05
4
@AeroX Obrigado por detectar essa falha. Decidi deixar minha solução como um aviso para qualquer um que possa ou tenha usado dayofyear, mas claramente editado para mostrar por que isso dá errado. Espero que seja adequado.
JonBrave
5

Como não há uma resposta simples que sempre dê a idade correta, eis o que eu vim.

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 

Isso obtém a diferença do ano entre a data de nascimento e a data atual. Subtrai um ano se a data de nascimento ainda não passou.

Precisas o tempo todo - independentemente dos anos bissextos ou da proximidade da data de nascimento.

O melhor de tudo - nenhuma função.

Punho de Hannover
fonte
3
SELECT ID,
Name,
DATEDIFF(yy,CONVERT(DATETIME, DOB),GETDATE()) AS AGE,
DOB
FROM MyTable
flayto
fonte
1
Você deseja getdate como o segundo argumento, e não o primeiro, caso contrário, obtém resultados numéricos negativos e rodadas com datas diferentes. Portanto, selecione data datada (yy, '20081231', getdate ()) relatará uma idade de 1, mas eles terão apenas 10 meses .
Andrew
1
Este cálculo fornece cálculos incorretos para pessoas que ainda não fizeram aniversário este ano.
3

A respeito:

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

Isso não evitaria todos esses problemas de arredondamento, truncamento e perturbação?

ub_coding
fonte
Seu cálculo não é preciso. Por exemplo: falha se você '1986-07-05 00:00:00'usar o DOB ​​e '2013-07-04 23:59:59'o tempo atual.
Drinovc 03/06/19
@ub_coding Você está oferecendo e responde ou faz outra pergunta?
Aaron C
this: DECLARE @DOB datetime SET @ DOB = '19760229' SELECT Datepart (aa, converter (datetime, '19770228') - @ DOB) -1900 = 1 o principal problema é fev 29 gap para a maioria das soluções, isso explica truncar arredondamentos etc. #
Leonardo Marques de Souza
3

Eu acredito que isso é semelhante aos outros postados aqui .... mas essa solução funcionou para os exemplos dos anos bissextos 29/02/1976 a 01/03/2011 e também funcionou para o caso do primeiro ano. Como 07/04 / 2011 a 03/03/2012, que o último postou sobre a solução de ano bissexto, não funcionou no caso de uso do primeiro ano.

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

Encontrado aqui .

lepert
fonte
3

Basta verificar se a resposta abaixo é viável.

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        
 )
Gopakumar N.Kurup
fonte
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
)

fonte: http://beginsql.wordpress.com/2012/04/26/how-to-calculate-age-in-sql-server/

celsowm
fonte
Uma maneira mais curta de fazer isso no SQL Server 2012+ é a seguinte e evita que as conversões sejam 112 e o piso não seja necessário:code: SELECT [Age] = (0+ FORMAT(@ToDate,'yyyyMMdd') - FORMAT(@DOB,'yyyyMMdd') ) /10000
ukgav
2

Eu tenho pensado muito e pesquisado sobre isso e tenho 3 soluções que

  • calcular a idade corretamente
  • são curtos (principalmente)
  • são (principalmente) muito compreensíveis.

Aqui estão os valores de teste:

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

Solução 1: Encontrei essa abordagem em uma biblioteca js. É o meu favorito.

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

Na verdade, está adicionando diferença de anos ao DOB ​​e, se for maior que a data atual, subtrai um ano. Simples né? A única coisa é que a diferença de anos é duplicada aqui.

Mas se você não precisar usá-lo em linha, pode escrevê-lo assim:

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

Solução 2: Este eu copiei originalmente de @ bacon-bits. É o mais fácil de entender, mas um pouco longo.

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

É basicamente calcular a idade como nós, humanos.


Solução 3: Meu amigo o refatorou para isso:

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

Este é o mais curto, mas é mais difícil de entender. 50é apenas um peso; portanto, a diferença do dia é importante apenas quando os meses são iguais. SIGNA função é transformar qualquer valor que obtiver em -1, 0 ou 1. CEILING(0.5 *é o mesmo que Math.max(0, value)mas não existe no SQL.

drinovc
fonte
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
Viswa Teja Kuncham
fonte
1
select floor((datediff(day,0,@today) - datediff(day,0,@birthdate)) / 365.2425) as age

Há muitas respostas 365,25 aqui. Lembre-se de como os anos bissextos são definidos:

  • A cada quatro anos
    • exceto a cada 100 anos
      • exceto a cada 400 anos
brianary
fonte
Excelente resposta. Para aqueles que estão curiosos aqui está uma explicação de por 365.2425 é o valor correto a ser utilizado: grc.nasa.gov/WWW/k-12/Numbers/Math/Mathematical_Thinking/...
vvvv4d
0

Que tal agora:

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
pajics
fonte
0

Tente isto

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'
user1194101
fonte
0

Experimente esta solução:

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
snoopy
fonte
0

Isso resolverá corretamente os problemas com o aniversário e o arredondamento:

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

SELECT DATEDIFF(YEAR, '0:0', getdate()-@dob)
Digi314
fonte
2
Não lida corretamente com os anos bissextos SELECT DATEDIFF (ANO, '0: 0', convert (datetime, '2014-02-28') -'2012-02-29 ') fornece 2, mas deve ser apenas 1
Peter Kerr
0

A solução de Ed Harper é a mais simples que encontrei, que nunca retorna a resposta errada quando o mês e o dia das duas datas têm 1 ou menos dias de diferença. Fiz uma ligeira modificação para lidar com idades negativas.

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
clovis517
fonte
0

A resposta marcada como correta está mais próxima da precisão, mas falha no seguinte cenário - onde o ano de nascimento é ano bissexto e dia após o mês de fevereiro

declare @ReportStartDate datetime = CONVERT(datetime, '1/1/2014'),
@DateofBirth datetime = CONVERT(datetime, '2/29/1948')

FLOOR(DATEDIFF(HOUR,@DateofBirth,@ReportStartDate )/8766)


OU

FLOOR(DATEDIFF(HOUR,@DateofBirth,@ReportStartDate )/8765.82) -- Divisor is more accurate than 8766

- A solução a seguir está me dando resultados mais precisos.

FLOOR(DATEDIFF(YEAR,@DateofBirth,@ReportStartDate) - (CASE WHEN DATEADD(YY,DATEDIFF(YEAR,@DateofBirth,@ReportStartDate),@DateofBirth) > @ReportStartDate THEN 1 ELSE 0 END ))

Funcionou em quase todos os cenários, considerando o ano bissexto, a data em 29 de fevereiro, etc.

Por favor, corrija-me se esta fórmula tiver alguma brecha.

Bhushan Borole
fonte
A fórmula final é quase exata, como nesta resposta stackoverflow.com/a/1572235/168747 . Mas o aqui é menos legível e contém desnecessário floor.
Marek
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
Alfredo Papirriqui
fonte
0

Aqui está como eu calculo a idade, dada a data de nascimento e a data atual.

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
Komengem
fonte
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
Vova
fonte
-1: estará errado a qualquer momento , month(compare) > month(dob)MAS day(compare) < day(dob), por exemplo select dbo.AgeAtDate('2000-01-14', '2016-02-12').
JonBrave 17/02
Você está certo, obrigado por este caso, ter atualizado função
Vova
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
Masum
fonte
0

Que tal uma solução com apenas funções de data, não matemática, não se preocupa com o ano bissexto

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
fcaserio
fonte
0

Depois de tentar MUITOS métodos, isso funciona 100% do tempo usando a moderna função MS SQL FORMAT em vez de converter para o estilo 112. Qualquer um deles funcionaria, mas esse é o menor código.

Alguém pode encontrar uma combinação de datas que não funcione? Eu não acho que exista um :)

--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
ukgav
fonte
-2

Usamos algo parecido aqui, mas depois medimos a idade média:

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

Observe que o ROUND está fora e não dentro. Isso permitirá que o AVG seja mais preciso e ROUND apenas uma vez. Tornando mais rápido também.

PCPGMR
fonte
-2
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 
Diego Sapigna
fonte
-2
SELECT CAST(DATEDIFF(dy, @DOB, GETDATE()+1)/365.25 AS int)
Alex
fonte
Vejo votos negativos, mas não vejo exemplos que provem que isso está quebrado.
Alex