Come calcolare l'età (in anni) in base alla data di nascita e getDate ()


172

Ho una tabella che elenca le persone insieme alla loro data di nascita (attualmente un nvarchar (25))

Come posso convertirlo in una data e quindi calcolare la loro età in anni?

I miei dati sono i seguenti

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

Vorrei vedere:

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

16
Perché stai memorizzando i valori di data come stringhe usando nvarchar (25) invece di usare la data nativa del database o il tipo di datetime?
Jesper,

La domanda è taggata 2005 non 2008, quindi il tipo "Date" nativo non è disponibile, ma sicuramente un datetime, e potrebbe essere discusso SmallDateTime poiché non è necessaria la precisione.
Andrew,

Ciao, il motivo per mantenere le date come varchar è perché lo sto importando da uno schema di server non SQL, ci sono stati alcuni problemi durante l'importazione come datetime (e gli altri formati di data) e varchar convertito ok
Jimmy,

7
@ James.Elsey, quindi hai avuto problemi con l'importazione e di conseguenza tutte le date sono valide? non si può mai essere sicuri a meno che non si usi un datetime o smalldatetime, con varchar, è possibile far funzionare l'importazione, ma si verificano altri problemi. Inoltre, non memorizzerei mai l'età, cambia ogni giorno, uso una vista
KM.

@KM Sì, si è verificato un problema durante l'importazione di tali dati come data, l'unica soluzione praticabile al momento era importarli come nvarchars. Questa selezione farà parte di un lavoro notturno, quindi la memorizzazione dell'età non dovrebbe essere un problema
Jimmy,

Risposte:


256

Esistono problemi con l'anno / i giorni bisestili e il seguente metodo, vedere l'aggiornamento di seguito:

prova questo:

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

PRODUZIONE:

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

(1 row(s) affected)

AGGIORNARE qui ci sono alcuni metodi più accurati:

MIGLIOR METODO PER GLI ANNI IN 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

puoi cambiare quanto sopra 10000 in10000.0 e ottenere decimali, ma non sarà così preciso come il metodo seguito.

MIGLIOR METODO PER GLI ANNI IN DECIMALE

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
Anche questa non è una soluzione esatta. Se ritengo che il mio @dob sia "1986-07-05 00:00:00" e lo eseguo (utilizzare un'altra variabile anziché GETDATE()) in "2013-07-04 23:59:59", si dice che Ho 27 anni, mentre in quel momento non lo sono ancora. Codice di esempio: 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
Ciò non è preciso in quanto presuppone 8766 ore all'anno, che raggiungono i 365,25 giorni. Dal momento che non ci sono anni con 365,25 giorni, questo sarà errato vicino alla data di nascita della persona più spesso di quanto sia corretto. Questo metodo sarà ancora più preciso.
Bacon Bits

1
Secondo commento di @Bacon Bits - questo spesso sarà sbagliato quando la data corrente è vicina alla data di nascita di una persona.
Flash

2
Penso che il primo blocco di testo renda confusa questa risposta. Se il tuo metodo aggiornato non presenta il problema con gli anni bisestili, suggerirei (se vuoi davvero mantenerlo del tutto) di spostarlo in fondo alla tua risposta.
ajbeaven

1
Se vuoi un calcolo ESATTO , DATEDIFF non lo farà @ShailendraMishra, perché si avvicina ai giorni, ecc. Ad esempio, select datediff(year, '2000-01-05', '2018-01-04')restituisce 18, non 17 come dovrebbe. Ho usato l'esempio sopra sotto il titolo "METODO MIGLIORE PER ANNI IN INT" e funziona perfettamente. Grazie!
openwonk

133

Devo buttarlo qui. Se converti la data usando lo stile 112 (yyyymmdd) in un numero puoi usare un calcolo come questo ...

(yyyyMMdd - yyyyMMdd) / 10000 = differenza tra anni interi

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

produzione

20091015    19800420    290595  29

14
Questo è quasi magico nel modo in cui risolve tutti i problemi dell'anno bisestile. Vale la pena notare che 112 è un numero speciale per la funzione CONVERT che formatta la data come yyyymmdd. Probabilmente non è ovvio per tutti quando lo guardi.
Derek Tomes,

5
Sei un genio!
Ercan il

Il mio team stava riscontrando un problema quando la data che stavamo usando per trovare l'età era lo stesso giorno della data con cui lo stiamo confrontando. Stavamo notando che quando erano lo stesso giorno (e se l'età sarebbe stata strana) l'età sarebbe stata fuori di uno. Questo ha funzionato perfettamente!
The Sheek Geek,

1
Questa è la risposta corretta: contrassegnala come tale.
Snympi,

4
Il codice più semplice / breve per questo stesso identico metodo di calcolo in 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

Ho usato questa query nel nostro codice di produzione per quasi 10 anni:

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

6
Non è male, ma non è del 100%, il 2007/10/16 segnalerà un'età di 2 anni il 15/10/2009
Andrew

4
Doh, ci stiamo perdendo l'ovvio, è dopo mezzogiorno, getdate restituisce un int quindi ovviamente arriverà. Copio incollato la tua risposta e la eseguo, quindi automaticamente usato getdate, non letterale.
Andrew,

2
Elegante e semplice Grazie!
William MB,

9
Se stiamo parlando dell'età umana, dovresti calcolarla nel modo in cui gli umani calcolano l'età. Non ha nulla a che fare con la velocità con cui si muove la terra e tutto con il calendario. Ogni volta che passano lo stesso mese e giorno della data di nascita, aumenti l'età di 1. Ciò significa che quanto segue è il più accurato perché rispecchia il significato degli umani quando dicono "età":DATEDIFF(yy, @BirthDate, GETDATE()) - CASE WHEN (MONTH(@BirthDate) >= MONTH(GETDATE())) AND DAY(@BirthDate) > DAY(GETDATE()) THEN 1 ELSE 0 END
Bacon Bits

5
Spiacente, quella sintassi è sbagliata. CASE WHEN (MONTH(@date) > MONTH(GETDATE())) OR (MONTH(@date) = MONTH(GETDATE()) AND DAY(@date) > DAY(GETDATE())) THEN 1 ELSE 0 END
Bacon Bits

31

Quindi molte delle soluzioni di cui sopra sono errate DateDiff (yy, @ Dob, @PassedDate) non considererà il mese e il giorno di entrambe le date. Anche prendere le freccette e confrontarle funziona solo se sono ordinate correttamente.

IL SEGUENTE CODICE FUNZIONA ED È MOLTO SEMPLICE:

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

Perché stai moltiplicando per 100? Questo funziona per me mentre sto cercando di replicare nel database ciò che esiste nella nostra libreria di codici, ma non sono riuscito a spiegare la tua funzione. Questa potrebbe essere una domanda stupida :)
Jen,

6
Grazie! Esattamente il codice che mi aspettavo qui. Questo è l' unico codice esattamente corretto in questo thread senza trasformazioni di stringa (brutte)! @Jen Richiede il mese e il giorno del DoB (come il 25 settembre) e lo trasforma in un valore intero 0925(o 925). Fa lo stesso con la data corrente (come il 16 dicembre diventa 1216) e quindi controlla se il valore intero DoB è già passato. Per creare questo numero intero, il mese deve essere moltiplicato per 100.
bartlaarhoven

Grazie @bartlaarhoven :)
Jen

Devo solo menzionare che mentre questo evita le trasformazioni di stringa, fa invece un sacco di casting. I miei test mostrano che non è significativamente più veloce della risposta di dotjoe e il codice è più dettagliato.
StriplingWarrior il

la risposta accettata ha una risposta INT molto più semplice:(CONVERT(int,CONVERT(char(8),@Now,112))-CONVERT(char(8),@Dob,112))/10000
KM.

19

Devi considerare il modo in cui il comando datiff arrotonda.

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>

Che mi sono adattato da qui .

Si noti che considererà il 28 febbraio come il compleanno di un balzo per anni non bisestili, ad esempio una persona nata il 29 febbraio 2020 sarà considerata di 1 anno il 28 febbraio 2021 anziché il 1 ° marzo 2021.


@Andrew - Corretto - Ho perso una delle sostituzioni
Ed Harper,

1
Versione semplificataSELECT DATEDIFF(year, DOB, getdate()) + CASE WHEN (DATEADD(year,DATEDIFF(year, DOB, getdate()) , DOB) > getdate()) THEN - 1 ELSE 0 END)
Peter

Questo è l'approccio corretto; Non capisco perché gli hack siano così votati.
Salman A

8

MODIFICA: QUESTA RISPOSTA È ERRATA. Lo lascio qui come avvertimento per chiunque sia tentato di usarlo dayofyear, con un'ulteriore modifica alla fine.


Se, come me, non vuoi dividere per giorni frazionari o rischiare errori di arrotondamento / anno bisestile, applaudo il commento di @Bacon Bits in un post sopra https://stackoverflow.com/a/1572257/489865 dove dice:

Se stiamo parlando dell'età umana, dovresti calcolarla nel modo in cui gli umani calcolano l'età. Non ha nulla a che fare con la velocità con cui si muove la terra e tutto con il calendario. Ogni volta che passano lo stesso mese e giorno della data di nascita, aumenti l'età di 1. Ciò significa che quanto segue è il più preciso perché rispecchia ciò che gli umani intendono quando dicono "età".

Quindi offre:

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

Ci sono diversi suggerimenti qui che coinvolgono il confronto del mese e del giorno (e alcuni sbagliano, non riuscendo a consentire ORcome correttamente qui!). Ma nessuno ha offerto dayofyear, che sembra così semplice e molto più breve. Io offro:

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

[Nota: da nessuna parte in SQL BOL / MSDN è ciò che DATEPART(dayofyear, ...)restituisce effettivamente documentato! Capisco che sia un numero compreso tra 1 e 366; soprattutto, non cambia in base alle impostazioni locali come da DATEPART(weekday, ...)& SET DATEFIRST.]


EDIT: Perché dayofyearva male : Come utente @AeroX ha commentato, se la nascita / data di inizio è dopo il febbraio in un anno non bisestile, l'età viene incrementato un giorno di anticipo quando la data / fine corrente è un anno bisestile, per esempio '2015-05-26', '2016-05-25'dà un all'età di 1 quando dovrebbe ancora essere 0. Il confronto tra dayofyearin diversi anni è chiaramente pericoloso. Quindi usare MONTH()ed DAY()è necessario dopo tutto.


Questo dovrebbe essere votato o addirittura contrassegnato come risposta. È corto, elegante e logicamente corretto.
z00l

1
Per tutti i nati dopo febbraio la loro età viene aumentata di un giorno in anticipo ogni anno bisestile usando il DayOfYearmetodo.
AeroX,

4
@AeroX Grazie per aver individuato questo difetto. Ho deciso di lasciare la mia soluzione come un avvertimento a chiunque potesse o abbia usato dayofyear, ma chiaramente modificato per mostrare perché va storto. Spero sia adatto.
JonBrave,

5

Dal momento che non esiste una risposta semplice che dia sempre l'età corretta, ecco cosa mi è venuto in mente.

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 

Questo ottiene la differenza dell'anno tra la data di nascita e la data corrente. Quindi sottrae un anno se la data di nascita non è ancora trascorsa.

Accurato in ogni momento, indipendentemente dagli anni bisestili o dalla data di nascita.

Meglio di tutto - nessuna funzione.


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

1
Volete getdate come secondo argomento, non il primo, altrimenti otterrete risultati in numeri negativi e round di dateiff, quindi selezionare dateiff (yy, '20081231', getdate ()) riporterà un'età di 1, ma avrebbero solo 10 mesi .
Andrew,

1
Questo calcolo fornisce calcoli errati per le persone che non hanno ancora avuto il loro compleanno quest'anno.

3

Che dire:

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

Ciò non eviterebbe tutti quei problemi di arrotondamento, troncamento e disinserimento?


Il tuo calcolo non è accurato. Ad esempio: fallisce se si prende '1986-07-05 00:00:00'per DOB e '2013-07-04 23:59:59'per l'ora corrente.
drinovc,

@ub_coding Offri e rispondi o fai un'altra domanda?
Aaron C

questo: DECLARE @DOB datetime SET @ DOB = '19760229' SELECT Datepart (yy, convert (datetime, '19770228') - @ DOB) -1900 = 1 il problema principale è fev 29 gap per la maggior parte delle soluzioni, questo spiega il troncamento arrotondamento ecc. .
Leonardo Marques de Souza

3

Credo che questo sia simile ad altri pubblicati qui .... ma questa soluzione ha funzionato per gli esempi dell'anno bisestile dal 29/02/1976 al 03/03/2011 e ha funzionato anche per il primo anno .. come il 07/04 / 2011 al 07/03/2012 che l'ultimo ha pubblicato sulla soluzione dell'anno bisestile non ha funzionato per quel caso d'uso del primo anno.

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

Trovato qui .


3

Basta verificare se la risposta di seguito è fattibile.

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
)

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


Un modo più breve per eseguire questa operazione in SQL Server 2012+ è il seguente ed evita i convertiti in 112 e non è necessario floor:code: SELECT [Age] = (0+ FORMAT(@ToDate,'yyyyMMdd') - FORMAT(@DOB,'yyyyMMdd') ) /10000
ukgav

2

Ho riflettuto e cercato molto su questo e ho 3 soluzioni

  • calcola l'età correttamente
  • sono brevi (principalmente)
  • sono (principalmente) molto comprensibili.

Ecco i valori di test:

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

Soluzione 1: ho trovato questo approccio in una libreria js. È il mio preferito.

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

In realtà sta aggiungendo la differenza in anni a DOB e se è maggiore della data corrente, sottrae un anno. Semplice vero? L'unica cosa è che la differenza negli anni è duplicata qui.

Ma se non hai bisogno di usarlo in linea puoi scriverlo in questo modo:

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

Soluzione 2: questo che ho copiato originariamente da @ bacon-bits. È il più facile da capire ma un po 'lungo.

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

Fondamentalmente sta calcolando l'età come facciamo noi umani.


Soluzione 3: il mio amico lo ha riformattato in questo:

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

Questo è il più breve ma è più difficile da capire. 50è solo un peso, quindi la differenza giornaliera è importante solo quando i mesi sono uguali. SIGNla funzione serve a trasformare qualsiasi valore che ottiene in -1, 0 o 1. CEILING(0.5 *è uguale a Math.max(0, value)ma non esiste nulla di simile in 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

Ci sono molte risposte 365.25 qui. Ricorda come sono definiti gli anni bisestili:

  • Ogni quattro anni
    • tranne ogni 100 anni
      • tranne ogni 400 anni

Risposta eccellente. Per coloro che sono curiosi ecco una spiegazione del perché 365.2425 è il valore corretto da usare: grc.nasa.gov/WWW/k-12/Numbers/Math/Mathematical_Thinking/…
vvvv4d

0

Cosa ne pensi di questo:

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

Prova questo

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

Prova questa soluzione:

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

Questo gestirà correttamente i problemi con il compleanno e l'arrotondamento:

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

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

2
Non gestisce correttamente gli anni bisestili SELEZIONA DATEDIFF (ANNO, '0: 0', converti (datetime, '2014-02-28') -'2012-02-29 ') dà 2, ma dovrebbe essere solo 1
Peter Kerr

0

La soluzione di Ed Harper è la più semplice che ho trovato che non restituisce mai la risposta sbagliata quando il mese e il giorno delle due date sono separati da 1 o meno giorni. Ho fatto una leggera modifica per gestire le età negative.

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

La risposta contrassegnata come corretta è più vicina alla precisione ma non riesce nel seguente scenario - in cui Anno di nascita è Anno bisestile e giorno dopo il mese di febbraio

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

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


O

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

- La seguente soluzione mi sta dando risultati più accurati.

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

Ha funzionato in quasi tutti gli scenari, considerando l'anno bisestile, la data come 29 feb, ecc.

Per favore, correggimi se questa formula ha qualche scappatoia.


La formula finale è quasi esatta come in questa risposta stackoverflow.com/a/1572235/168747 . Ma quello qui è meno leggibile e contiene inutili 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

0

Ecco come calcolo l'età data una data di nascita e data corrente.

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: sarà sbagliato in qualsiasi momento month(compare) > month(dob)MA day(compare) < day(dob), ad es select dbo.AgeAtDate('2000-01-14', '2016-02-12').
JonBrave,

Hai ragione, grazie per questo caso, hai una funzione aggiornata
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

0

Che dire di una soluzione con solo funzioni di data, non matematica, non preoccupazioni per l'anno bisestile

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

Dopo aver provato MOLTI metodi, questo funziona il 100% delle volte usando la moderna funzione FORMATO MS SQL invece di convertirlo in stile 112. O funzionerebbe, ma questo è il codice minimo.

Qualcuno può trovare una combinazione di date che non funziona? Non penso ce ne sia uno :)

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

Abbiamo usato qualcosa come qui, ma poi prendendo l'età media:

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

Si noti che il ROUND è all'esterno anziché all'interno. Ciò consentirà ad AVG di essere più accurato e di ROUND solo una volta. Rendendolo anche più veloce.


-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 

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

Vedo i voti negativi ma non vedo esempi che dimostrino che questo è rotto.
Alex
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.