197 votes

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

J'ai une table qui liste les personnes avec leur date de naissance (actuellement un nvarchar(25)).

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

Mes données sont les suivantes

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

16 votes

Pourquoi stockez-vous les valeurs de date sous forme de chaînes en utilisant nvarchar(25) au lieu d'utiliser le type natif de date ou datetime de la base de données ?

0 votes

La question étant étiquetée 2005 et non 2008, le type natif 'Date' n'est pas disponible, mais il s'agit bien d'une date, et l'on pourrait dire SmallDateTime puisque vous n'avez pas besoin de précision.

0 votes

Bonjour, la raison pour laquelle j'ai gardé les dates en tant que varchar est que j'importe ceci à partir d'un schéma non-SQL Server, il y a eu quelques problèmes en les important en tant que datetime (et les autres formats de date) et varchar a été converti sans problème.

285voto

KM. Points 51800

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

Essayez ceci :

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)

UPDATE voici des méthodes plus précises :

MEILLEURE MÉTHODE POUR LES ANNÉES 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 modifier ce qui précède 10000 à 10000.0 et obtenir des décimales, mais elle ne sera pas aussi précise que la méthode ci-dessous.

MEILLEURE MÉTHODE POUR LES ANNÉES EN DÉCIMAL

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

0 votes

Il y avait des problèmes pour importer les varchar en tant que datetime à l'origine (d'où le DOB est stocké comme un varchar), mais vous pouvez utiliser cette méthode ? En quoi cela a-t-il un sens ? Ne rencontrez-vous pas les mêmes "problèmes" ici ?

0 votes

@Kirk Broadhurst, il est possible qu'ils les aient chargés dans cette table en utilisant varchar, et qu'ils aient ensuite effectué un second passage pour les corriger. Il serait préférable de les charger dans une table de travail en utilisant varchar, puis de les corriger avant de les déplacer vers la table réelle où ils sont datetime.

25 votes

Il ne s'agit pas non plus d'une solution exacte. Si je prends mon propre @dob comme étant '1986-07-05 00:00:00' et que j'exécute ceci (en utilisant une autre variable au lieu de GETDATE() ) le '2013-07-04 23:59:59', il indique que j'ai 27 ans, alors qu'à ce moment-là, je ne les ai pas encore. Exemple de code : 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

158voto

dotjoe Points 11959

Je dois en parler. Si vous convertir la date en utilisant le style 112 (yyyymmdd) à un nombre vous pouvez utiliser un calcul comme ceci...

(aaaaMMj - aaaaMMj) / 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

18 votes

C'est presque magique dans la mesure où cela résout tous les problèmes liés aux années bissextiles. Il convient peut-être de noter que 112 est un nombre spécial pour la fonction CONVERT qui formate la date sous la forme aaaammjj. Ce n'est peut-être pas évident pour tout le monde quand on y regarde de plus près.

5 votes

Vous êtes un génie !

0 votes

Mon équipe a rencontré un problème lorsque la date utilisée pour déterminer l'âge était le même jour que la date à laquelle nous la comparons. Nous avions remarqué que lorsqu'elles étaient le même jour (et si l'âge était impair), l'âge était décalé d'une unité. Cela a parfaitement fonctionné !

48voto

J__ Points 2312

J'utilise cette requête dans notre code de production depuis près de 10 ans :

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

7 votes

Ce n'est pas mauvais, mais ce n'est pas 100%, 2007/10/16 signalera un âge de 2 le 2009/10/15

0 votes

Ce n'est pas le cas sur le mien, il renvoie 1 qui est la bonne réponse.

0 votes

Bizarre, j'obtiens 2, Today cast as int is 40100, 2007/10/16 is 36939, la différence est de 731, divisé par 365.25 donne 2.001368 ce qui donne 2, mais l'anniversaire est dans 1 jour donc devrait donner le 1 que vous obtenez. Je l'exécute sur 2005 mais cela ne devrait pas faire de différence.

33voto

user2634514 Points 71

Beaucoup des solutions ci-dessus sont donc erronées DateDiff(yy,@Dob, @PassedDate) ne tiendra pas compte du mois et du jour des deux dates. De plus, prendre les parties de la fléchette et les comparer ne fonctionne que si elles sont correctement ordonnées.

LE CODE SUIVANT FONCTIONNE ET 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

0 votes

Pourquoi tu multiplies par 100 ? Cela fonctionne pour moi car j'essaie de reproduire dans la base de données ce qui existe dans notre bibliothèque de codes - mais je n'ai pas pu expliquer votre fonction. C'est peut-être une question stupide :)

7 votes

Merci ! Exactement le code que j'attendais ici. C'est le seulement exactement correct dans ce fil de discussion sans transformations de chaînes (laides) ! @Jen Il prend le mois et le jour de la DoB (comme le 25 septembre) et le transforme en une valeur entière. 0925 (ou 925 ). Il fait de même avec la date actuelle (comme le 16 décembre devient 1216 ) et vérifie ensuite si la valeur entière de la DoB est déjà passée. Pour créer ce nombre entier, le mois doit être multiplié par 100.

0 votes

Merci @bartlaarhoven :)

28voto

Ed Harper Points 13289

Vous devez prendre en compte la façon dont le commandement du datavisualiste tourne.

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 j'ai adapté de ici .

Notez que le 28 février sera considéré comme l'anniversaire d'un bissextile pour les années non bissextiles. Par exemple, une personne née le 29 février 2020 sera considérée comme ayant un an le 28 février 2021 au lieu du 1er mars 2021.

0 votes

@Andrew - Corrigé - J'ai manqué une des substitutions.

1 votes

Version simplifiée SELECT DATEDIFF(year, DOB, getdate()) + CASE WHEN (DATEADD(year,DATEDIFF(year, DOB, getdate()) , DOB) > getdate()) THEN - 1 ELSE 0 END)

1 votes

C'est l'approche correcte ; je ne comprends pas pourquoi les hacks sont autant upvoted.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X