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
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
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
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
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
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
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.
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.
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 .
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?
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
)
SELECT ID,
Name,
DATEDIFF(yy,CONVERT(DATETIME, DOB),GETDATE()) AS AGE,
DOB
FROM MyTable
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/
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
J'ai beaucoup réfléchi et cherché à ce sujet et j'ai 3 solutions
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.
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
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
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'
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
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)
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:
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
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
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
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
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.
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
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