如何根据出生日期和getDate()计算年龄(以年为单位)

Jim*_*mmy 157 sql-server select sql-server-2005 date

我有一张桌子列出了人们的出生日期(目前是nvarchar(25))

如何将其转换为日期,然后计算年龄?

我的数据如下

ID    Name   DOB
1     John   1992-01-09 00:00:00
2     Sally  1959-05-20 00:00:00
Run Code Online (Sandbox Code Playgroud)

我想看看:

ID    Name   AGE  DOB
1     John   17   1992-01-09 00:00:00
2     Sally  50   1959-05-20 00:00:00
Run Code Online (Sandbox Code Playgroud)

KM.*_*KM. 238

闰年/日期存在问题以及以下方法,请参阅以下更新:

试试这个:

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
Run Code Online (Sandbox Code Playgroud)

OUTPUT:

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

(1 row(s) affected)
Run Code Online (Sandbox Code Playgroud)

这里更新一些更准确的方法:

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
Run Code Online (Sandbox Code Playgroud)

你可以改变上面1000010000.0和得到小数,但它不会像下面的方法那样准确.

十年来最好的方法

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
Run Code Online (Sandbox Code Playgroud)

  • 这也不是一个确切的解决方案.如果我把我自己的@dob变为'1986-07-05 00:00:00'并且我将在'2013-07-04 23:59上执行此操作(使用另一个变量而不是`GETDATE()`): 59'它说我27岁,而在那一刻,我还没有.示例代码:`declare @startDate nvarchar(100)='1986-07-05 00:00:00'声明@endDate nvarchar(100)='2013-07-04 23:59:59'SELECT DATEDIFF(小时,@ startDate,@ endDate)/8766.0 AS AgeYearsDecimal,CONVERT(int,ROUND(DATEDIFF(小时,@ startDate,@ endDate)/8766.0,0))AS AgeYearsIntRound,DATEDIFF(小时,@ startDate,@ endDate)/ 8766 AS AgeYearsIntTrunc` (22认同)
  • 这是不准确的,因为它假设每年8766小时,这可以达到365.25天.由于没有365.25天的年数,这个人的出生日期附近的错误往往不正确.[此方法](http://stackoverflow.com/questions/57599/how-to-calculate-age-in-t-sql-with-years-months-and-days)仍然会更准确. (19认同)
  • 谢谢KM,我正在使用截断选项,工作一个款待! (2认同)
  • 我认为第一个文本块使得这个答案令人困惑.如果您更新的方法没有闰年的问题,我建议(如果您真的想保留它)将其移到答案的底部. (2认同)

dot*_*joe 116

得扔掉这个.如果您使用112样式(yyyymmdd)日期转换为数字,则可以使用此类计算...

(yyyyMMdd - yyyyMMdd)/ 10000 =全年的差异

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
Run Code Online (Sandbox Code Playgroud)

产量

20091015    19800420    290595  29
Run Code Online (Sandbox Code Playgroud)

  • 这对于如何解决所有闰年问题几乎是神奇的.值得注意的是,112是CONVERT函数的特殊数字,它将日期格式化为yyyymmdd.当你看到它时,对每个人来说可能并不明显. (13认同)
  • 这个答案应该标记为*答案! (8认同)
  • 你是个天才! (5认同)
  • 对于SQL Server 2012+中完全相同的计算方法,最简单/最短的代码是`code:SELECT [Age] =(0+ FORMAT(@ as_of,'yyyyMMdd')-FORMAT(@ bday,'yyyyMMdd'))/ 10000 -0+部分告诉SQL将char(8)计算为数字` (2认同)

J__*_*J__ 40

我在生产代码中使用了这个查询近10年:

SELECT FLOOR((CAST (GetDate() AS INTEGER) - CAST(Date_of_birth AS INTEGER)) / 365.25) AS Age
Run Code Online (Sandbox Code Playgroud)

  • 如果我们谈论人类年龄,你应该按照人类计算年龄的方式来计算它.它与地球的移动速度和与日历有关的一切都没有任何关系.每当相同的月份和日期过去时,你将年龄增加1.这意味着以下是最准确的,因为它反映了人们说"年龄"时的意思:`DATEDIFF(yy,@ BirthDate,GETDATE) ()) - CASE WHEN(月(@BirthDate)> =月(GETDATE()))和DAY(@BirthDate)> DAY(GETDATE())然后1 ELSE 0 END (9认同)
  • 这不错,但不是100%,2007/10/16将在2009/10/15报告2岁 (5认同)
  • 对不起,那个语法错了.`CASE WHEN(月(@日期)>月(GETDATE()))或(月(@日期)=月(GETDATE())和DAY(@日期)> DAY(GETDATE()))然后1 ELSE 0 END ` (5认同)
  • Doh,我们错过了明显的,它是在中午之后,getdate返回一个int,所以当然会四舍五入.我复制粘贴你的答案并运行它,所以自动使用getdate,而不是文字. (4认同)
  • 优雅而简约.谢谢! (2认同)

小智 30

上面的许多解决方案都是错误的DateDiff(yy,@ Dob,@ PassedDate)不会考虑两个日期的月份和日期.如果正确订购,也可以使用飞镖部件并进行比较.

以下代码的工作原理非常简单:

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
Run Code Online (Sandbox Code Playgroud)

  • 谢谢!正是我期待的代码.这是没有(丑陋)字符串转换的**线程中唯一完全正确的**代码!@Jen需要DoB的月份和日期(如9月25日)并将其变为整数值"0925"(或"925").它与当前日期相同(如12月16日变为`1216`)然后检查DoB整数值是否已经过去.要创建此整数,月份应乘以100. (6认同)

Ed *_*per 15

您需要考虑datediff命令的舍入方式.

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>
Run Code Online (Sandbox Code Playgroud)

我从这里改编

  • 简化版本`SELECT DATEDIFF(year, DOB, getdate()) + CASE WHEN (DATEADD(year,DATEDIFF(year, DOB, getdate()) , DOB) &gt; getdate()) THEN - 1 ELSE 0 END)` (5认同)
  • 这才是正确的做法;我不明白为什么黑客如此受好评。 (3认同)
  • 它还比使用 FORMAT 快 16 倍以上。 (2认同)

Jon*_*ave 8

编辑:这个答案是不正确的. 我把它留在这里作为对任何想要使用的人的警告dayofyear,最后进行进一步的编辑.


如果像我一样,你不想除以小数天或冒险四舍五入/闰年错误,我赞成@Bacon Bits在/sf/answers/110058021/上的帖子中发表评论,他说:

如果我们谈论人类年龄,你应该按照人类计算年龄的方式来计算它.它与地球的移动速度和与日历有关的一切都没有任何关系.每当同一个月和一天都作为出生日期过去时,你将年龄增加1.这意味着以下是最准确的,因为它反映了人们说"年龄"时的意思.

然后他提供:

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
Run Code Online (Sandbox Code Playgroud)

这里有几个建议涉及比较月份和日期(有些错误,未能在OR这里正确地允许!).但没有人提供dayofyear,这似乎是如此简单和短得多.我提供:

DATEDIFF(year, @date, GETDATE()) -
CASE WHEN DATEPART(dayofyear, @date) > DATEPART(dayofyear, GETDATE()) THEN 1 ELSE 0 END
Run Code Online (Sandbox Code Playgroud)

[注意:SQL BOL/MSDN中没有任何内容可以DATEPART(dayofyear, ...)返回实际记录的内容!我理解它是1--366范围内的数字; 最重要的是,它并没有按区域按更改DATEPART(weekday, ...)&SET DATEFIRST]


编辑: 为什么dayofyear出错:正如用户@AeroX评论的那样,如果出生/开始日期是非闰年的2月之后,当前/结束日期是闰年时,年龄会提前一天递增,例如'2015-05-26','2016-05-25'给出当它应该仍为0时,年龄为1岁.比较dayofyear不同年份显然是危险的.所以使用MONTH()并且DAY()毕竟是必要的.

  • @AeroX感谢您发现这个漏洞.我决定将我的解决方案留给任何可能或曾经使用过"dayofyear"的人发出警告,但是要清楚地编辑以说明它出错的原因.我希望这是合适的. (3认同)
  • 对于二月之后出生的每个人,他们的年龄会在每个闰年使用 `DayOfYear` 方法提前一天增加。 (2认同)

lep*_*ert 6

我相信这与此处发布的其他解决方案类似......但该解决方案适用于 02/29/1976 至 03/01/2011 的闰年示例,并且也适用于第一年的案例......如 07/04 /2011 到 07/03/2012,最后一个发布的关于闰年的解决方案不适用于第一年的用例。

SELECT FLOOR(DATEDIFF(DAY, @date1 , @date2) / 365.25)
Run Code Online (Sandbox Code Playgroud)

这里找到。


Han*_*ist 5

由于没有一个简单的答案总是给出正确的年龄,这就是我想出的.

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 
Run Code Online (Sandbox Code Playgroud)

这将获得出生日期和当前日期之间的年份差异.如果生日还没有过去,那么它会减去一年.

始终准确 - 无论闰年或接近生日.

最重要的是 - 没有功能.


dri*_*ovc 5

我对此做了很多思考和搜索,我有 3 个解决方案

  • 正确计算年龄
  • 很短(大部分)
  • (大部分)非常容易理解。

以下是测试值:

DECLARE @NOW DATETIME = '2013-07-04 23:59:59' 
DECLARE @DOB DATETIME = '1986-07-05' 
Run Code Online (Sandbox Code Playgroud)

解决方案1:我在一个js库中找到了这种方法。这是我最喜欢的。

DATEDIFF(YY, @DOB, @NOW) - 
  CASE WHEN DATEADD(YY, DATEDIFF(YY, @DOB, @NOW), @DOB) > @NOW THEN 1 ELSE 0 END
Run Code Online (Sandbox Code Playgroud)

它实际上是在 DOB 上加上年份差异,如果它大于当前日期,则减去一年。简单吧?唯一的问题是这里重复了年份的差异。

但如果你不需要内联使用它,你可以这样写:

DECLARE @AGE INT = DATEDIFF(YY, @DOB, @NOW)
IF DATEADD(YY, @AGE, @DOB) > @NOW
SET @AGE = @AGE - 1
Run Code Online (Sandbox Code Playgroud)

解决方案 2:这是我最初从@bacon-bits 复制的。这是最容易理解的,但有点长。

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
Run Code Online (Sandbox Code Playgroud)

它基本上是像我们人类一样计算年龄。


解决方案3:我的朋友将其重构为:

DATEDIFF(YY, @DOB, @NOW) - 
  CEILING(0.5 * SIGN((MONTH(@DOB) - MONTH(@NOW)) * 50 + DAY(@DOB) - DAY(@NOW)))
Run Code Online (Sandbox Code Playgroud)

这是最短但最难理解的。50只是一个重量,因此只有当月份相同时,日差才重要。SIGN函数用于将它得到的任何值转换为 -1、0 或 1。CEILING(0.5 *与 相同Math.max(0, value),但 SQL 中没有这样的东西。


bri*_*ary 5

select floor((datediff(day,0,@today) - datediff(day,0,@birthdate)) / 365.2425) as age
Run Code Online (Sandbox Code Playgroud)

这里有很多365.25的答案。记住闰年是如何定义的:

  • 每四年一次
    • 除了每100年一次
      • 除了每 400 年一次