web-dev-qa-db-fra.com

Comment calculer l'âge (en années) en fonction de la date de naissance et de getDate ()

J'ai un tableau listant les personnes avec leur date de naissance (actuellement nvarchar (25))

Comment puis-je convertir cela en date, puis calculer leur âge en années?

Mes données se présentent comme suit

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

J'aimerais voir:

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

Il y a des problèmes avec les années bissextiles/jours et la méthode suivante, voir la mise à jour ci-dessous:

essaye ça:

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

SORTIE:

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

(1 row(s) affected)

UPDATEvoici quelques méthodes plus précises:

MEILLEURE METHODE POUR ANNEES EN 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

vous pouvez changer le 10000 ci-dessus en 10000.0 et obtenir des décimales, mais ce ne sera pas aussi précis que la méthode ci-dessous.

MEILLEURE METHODE DES ANNEES EN 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
218
KM.

Je dois jeter celui-ci là-bas. Si vous convertissez la date en utilisant le style 112 (aaaammjj) en nombre, vous pouvez utiliser un calcul comme celui-ci ...

(aaaaMMjj - aaaaaMMjj)/10000 = différence en années complètes

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

sortie

20091015    19800420    290595  29
99
dotjoe

J'ai utilisé cette requête dans notre code de production pendant près de 10 ans:

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

La plupart des solutions ci-dessus sont incorrectes. DateDiff (yy, @ Dob, @PassedDate) ne tient pas compte du mois et du jour des deux dates. De plus, prendre les parties de la fléchette et comparer les résultats ne fonctionne que s’ils sont correctement commandés.

LE CODE SUIVANT FONCTIONNE ET IS est très simple:

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

Vous devez tenir compte de la façon dont la commande datiff se règle.

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>

Ce que j'ai adapté de ici

15
Ed Harper

EDIT: CETTE REPONSE IS EST INCORRECTE.Je le laisse ici comme un avertissement à quiconque serait tenté d'utiliser dayofyear, avec une modification supplémentaire à la fin.


Si, comme moi, vous ne souhaitez pas diviser par fraction de jours ni risquer d'arrondir/d'année bissextile, j'applaudis le commentaire de @Bacon Bits dans un message ci-dessus https://stackoverflow.com/a/1572257/489865 où il dit:

Si nous parlons d’âge humain, vous devriez le calculer comme suit les humains calculent l'âge. Cela n'a rien à voir avec la rapidité de la terre se déplace et tout à voir avec le calendrier. Chaque fois les mêmes mois et jour s'écoulent comme date de naissance, vous incrémentez l'âge de 1 . Cela signifie que ce qui suit est le plus précis, car il reflète ce que les humains veulent dire quand ils disent "âge".

Il propose ensuite:

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

Il y a plusieurs suggestions ici qui consistent à comparer le mois et le jour (et certaines se trompent, en ne tenant pas compte de la variable OR aussi correctement ici!). Mais personne n’a offert dayofyear, ce qui semble si simple et beaucoup plus court. J'offre:

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

[Remarque: Nulle part dans SQL BOL/MSDN n’est ce que DATEPART(dayofyear, ...) renvoie réellement documenté! Je crois comprendre qu’il s’agit d’un nombre compris entre 1 et 366; le plus important est que pas change selon les paramètres régionaux conformément à DATEPART(weekday, ...) & SET DATEFIRST.]


EDIT: _ ​​Pourquoi dayofyear ne va pas: Comme l'a commenté @AeroX, si la date de naissance/début est postérieure à février dans une année non bissextile, l'âge est incrémenté un jour plus tôt lorsque la date de fin est une année bissextile, par exemple '2015-05-26', '2016-05-25' donne un âge de 1 quand il devrait encore être 0. Comparer le dayofyear dans différentes années est clairement dangereux. Donc, utiliser MONTH() et DAY() est nécessaire après tout.

8
JonBrave

Puisqu'il n'y a pas une réponse simple qui donne toujours l'âge correct, voici ce que j'ai proposé. 

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 

Cela obtient la différence d'année entre la date de naissance et la date actuelle. Ensuite, il soustrait un an si la date de naissance n’est pas encore passée.

Précis tout le temps - indépendamment des années bissextiles ou de la date de naissance. 

Le meilleur de tous - pas de fonction.

5
Hannover Fist

Je pense que cela ressemble aux autres publiés ici .... mais cette solution a fonctionné pour les exemples de l'année bissextile du 29/02/1976 au 03/01/2011 et a également fonctionné pour le cas la première année .. like 07/04/2011 au 07/03/2012 que le dernier a publié sur la solution pour l'année bissextile ne fonctionne pas pour ce cas d'utilisation de la première année.

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

Trouvé ici .

3
lepert

Qu'en est-il de:

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

Cela ne permettrait-il pas d'éviter tous ces problèmes d'arrondissement, de tronquage et de résolution?

3
ub_coding

Il suffit de vérifier si la réponse ci-dessous est réalisable. 

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        
 )
3
Gopakumar N.Kurup
SELECT ID,
Name,
DATEDIFF(yy,CONVERT(DATETIME, DOB),GETDATE()) AS AGE,
DOB
FROM MyTable
2
flayto
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
)

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

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

J'ai beaucoup réfléchi et cherché à ce sujet et j'ai 3 solutions 

  • calculer correctement l'âge 
  • sont courts (la plupart du temps)
  • sont (la plupart du temps) très compréhensibles.

Voici les valeurs de test:

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

Solution 1: J'ai trouvé cette approche dans une bibliothèque js. C'est mon prefere. 

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

En fait, elle ajoute la différence en années à la date de naissance et, si elle est supérieure à la date actuelle, soustrait une année. Simple droit? La seule chose est que la différence en années est dupliquée ici. 

Mais si vous n'avez pas besoin de l'utiliser en ligne, vous pouvez l'écrire comme ceci:

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

Solution 2: Celui-ci que j'ai copié à l'origine de @ bacon-bits. C'est le plus facile à comprendre mais un peu long.

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

C'est fondamentalement calculer l'âge comme nous, les humains. 


Solution 3: Mon ami l'a reformulé dans ceci:

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

Celui-ci est le plus court mais le plus difficile à comprendre. 50 est juste un poids donc la différence de jours n’est importante que lorsque les mois sont les mêmes. La fonction SIGN sert à transformer n'importe quelle valeur obtenue en -1, 0 ou 1. CEILING(0.5 * est identique à Math.max(0, value) mais il n'y a rien de tel dans SQL.

1
drinovc

La solution d'Ed Harper est la plus simple que j'ai trouvée et qui ne renvoie jamais la mauvaise réponse lorsque le mois et le jour des deux dates sont séparés d'un jour ou moins. J'ai fait une légère modification pour gérer les âges négatifs.

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

Essaye ça 

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
user1194101

Après avoir essayé plusieurs méthodes, cela fonctionne 100% du temps en utilisant la fonction moderne MS SQL FORMAT au lieu de convertir en style 112. Cela fonctionnerait mais ce serait le moins de code.

Quelqu'un peut-il trouver une combinaison de date qui ne fonctionne pas? Je ne pense pas qu'il y en ait un :)

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

Cela gérera correctement les problèmes d'anniversaire et d'arrondi:

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

SELECT DATEDIFF(YEAR, '0:0', getdate()-@dob)
0
Digi314
select floor((datediff(day,0,@today) - datediff(day,0,@birthdate)) / 365.2425) as age

Il y a beaucoup de réponses 365,25 ici. Rappelez-vous comment les années bissextiles sont définies:

  • Tous les quatre ans
    • sauf tous les 100 ans
      • sauf tous les 400 ans
0
brianary

Qu'en est-il d'une solution avec uniquement des fonctions de date, pas de maths, pas d'inquiétude pour l'année bissextile

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

Voici comment je calcule l'âge en fonction d'une date de naissance et d'une date actuelle.

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
Komengem

La réponse marquée comme correcte est plus proche de la précision, mais elle échoue dans le scénario suivant - où L'année de naissance est l'année bissextile et le jour après le mois de février 

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

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


OR 

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

- Suivre la solution me donne des résultats plus précis.

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

Cela a fonctionné dans presque tous les scénarios, année bissextile, date du 29 février, etc.

S'il vous plaît, corrigez-moi si cette formule présente une échappatoire. 

0
Bhushan Borole

Que dis-tu de ça:

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
pajics

Essayez cette solution:

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
snoopy