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
我想看看:
ID    Name   AGE  DOB
1     John   17   1992-01-09 00:00:00
2     Sally  50   1959-05-20 00:00:00
KM.*_*KM. 238
闰年/日期存在问题以及以下方法,请参阅以下更新:
试试这个:
Run Code Online (Sandbox Code Playgroud)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 AgeYearsIntTruncOUTPUT:
Run Code Online (Sandbox Code Playgroud)AgeYearsDecimal AgeYearsIntRound AgeYearsIntTrunc --------------------------------------- ---------------- ---------------- 17.767054 18 17 (1 row(s) affected)
这里更新一些更准确的方法:
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
你可以改变上面10000的10000.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
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
产量
20091015    19800420    290595  29
J__*_*J__ 40
我在生产代码中使用了这个查询近10年:
SELECT FLOOR((CAST (GetDate() AS INTEGER) - CAST(Date_of_birth AS INTEGER)) / 365.25) AS Age
小智 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
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>
我从这里改编
编辑:这个答案是不正确的. 我把它留在这里作为对任何想要使用的人的警告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
这里有几个建议涉及比较月份和日期(有些错误,未能在OR这里正确地允许!).但没有人提供dayofyear,这似乎是如此简单和短得多.我提供:
DATEDIFF(year, @date, GETDATE()) -
CASE WHEN DATEPART(dayofyear, @date) > DATEPART(dayofyear, GETDATE()) THEN 1 ELSE 0 END
[注意: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()毕竟是必要的.
我相信这与此处发布的其他解决方案类似......但该解决方案适用于 02/29/1976 至 03/01/2011 的闰年示例,并且也适用于第一年的案例......如 07/04 /2011 到 07/03/2012,最后一个发布的关于闰年的解决方案不适用于第一年的用例。
SELECT FLOOR(DATEDIFF(DAY, @date1 , @date2) / 365.25)
在这里找到。
由于没有一个简单的答案总是给出正确的年龄,这就是我想出的.
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 
这将获得出生日期和当前日期之间的年份差异.如果生日还没有过去,那么它会减去一年.
始终准确 - 无论闰年或接近生日.
最重要的是 - 没有功能.
我对此做了很多思考和搜索,我有 3 个解决方案
以下是测试值:
DECLARE @NOW DATETIME = '2013-07-04 23:59:59' 
DECLARE @DOB DATETIME = '1986-07-05' 
解决方案1:我在一个js库中找到了这种方法。这是我最喜欢的。
DATEDIFF(YY, @DOB, @NOW) - 
  CASE WHEN DATEADD(YY, DATEDIFF(YY, @DOB, @NOW), @DOB) > @NOW THEN 1 ELSE 0 END
它实际上是在 DOB 上加上年份差异,如果它大于当前日期,则减去一年。简单吧?唯一的问题是这里重复了年份的差异。
但如果你不需要内联使用它,你可以这样写:
DECLARE @AGE INT = DATEDIFF(YY, @DOB, @NOW)
IF DATEADD(YY, @AGE, @DOB) > @NOW
SET @AGE = @AGE - 1
解决方案 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
它基本上是像我们人类一样计算年龄。
解决方案3:我的朋友将其重构为:
DATEDIFF(YY, @DOB, @NOW) - 
  CEILING(0.5 * SIGN((MONTH(@DOB) - MONTH(@NOW)) * 50 + DAY(@DOB) - DAY(@NOW)))
这是最短但最难理解的。50只是一个重量,因此只有当月份相同时,日差才重要。SIGN函数用于将它得到的任何值转换为 -1、0 或 1。CEILING(0.5 *与 相同Math.max(0, value),但 SQL 中没有这样的东西。
select floor((datediff(day,0,@today) - datediff(day,0,@birthdate)) / 365.2425) as age
这里有很多365.25的答案。记住闰年是如何定义的: