使用SQL计算以年为单位的确切日期差

使用SQL计算以年为单位的确切日期差,sql,sql-server,datediff,Sql,Sql Server,Datediff,我收到报告,其中数据是自动发送到数据库的ETL。我提取并转换一些数据,将其加载到其他地方。我需要做的一件事是一个DATEDIFF,但年份需要精确(即4.6年,而不是四舍五入到5年) 以下是我的脚本: select *, DATEDIFF (yy, Begin_date, GETDATE()) AS 'Age in Years' from Report_Stage; “以年为单位的年龄”列正在四舍五入。如何获得以年为单位的确切日期?您是否尝试过以月为单位计算差值,然后以这种方式计算年份?例如,3

我收到报告,其中数据是自动发送到数据库的
ETL
。我提取并转换一些数据,将其加载到其他地方。我需要做的一件事是一个
DATEDIFF
,但年份需要精确(即4.6年,而不是四舍五入到5年)

以下是我的脚本:

select *, DATEDIFF (yy, Begin_date, GETDATE()) AS 'Age in Years'
from Report_Stage;

“以年为单位的年龄”列正在四舍五入。如何获得以年为单位的确切日期?

您是否尝试过以月为单位计算差值,然后以这种方式计算年份?例如,30个月/12年将是2.5年

编辑:此SQL查询包含几种计算日期差的方法:

SELECT CONVERT(date, GetDate() - 912) AS calcDate
      ,DATEDIFF(DAY, GetDate() - 912, GetDate()) diffDays
      ,DATEDIFF(DAY, GetDate() - 912, GetDate()) / 365.0 diffDaysCalc
      ,DATEDIFF(MONTH, GetDate() - 912, GetDate()) diffMonths
      ,DATEDIFF(MONTH, GetDate() - 912, GetDate()) / 12.0 diffMonthsCalc
      ,DATEDIFF(YEAR, GetDate() - 912, GetDate()) diffYears
所有的
datediff()
都是计算两个日期之间跨越的时段边界数

datediff(yy,'31 Dec 2013','1 Jan 2014')
返回1

如果计算两个日期(以天为单位)之间的差值,然后除以400年跨度(365.2425)内一个日历年(以天为单位)的平均长度,则会得到更准确的结果:

比如说,

select datediff(day,'1 Jan 2000' ,'18 April 2014') / 365.2425

返回
14.29461248
-只需将其四舍五入到所需的精度。

我认为用365.2425除法不是一种很好的方法。没有任何除法可以完全精确地完成此操作(使用365.25也有问题)

我知道下面的脚本计算了准确的日期差(虽然可能不是最快的方法):

你也许可以用除法计算小范围,但为什么要冒险呢

下面的脚本可以帮助测试yeardiff函数(只需将datediff(day,@d1,@d2)/365.2425作为int)交换到函数的任何位置):

declare@d1日期时间集@d1='1900-01-01'
而(@d1<'2016-01-01')
开始
声明@d2日期时间集@d2='2016-04-01'
而(@d2>='1900-01-01')
开始
如果(@d1@d2)
开始
选择“不是一年!!”、@d1、@d2、cast(datediff(day、@d1、@d2)/365.2425作为int)
结束
set@d2=dateadd(第-1天,@d2)
结束
set@d1=dateadd(第1天,@d1)
结束

我找到了一个更好的解决方案。这就假设第一个日期小于或等于第二个日期

declare @dateTable table (date1 datetime, date2 datetime)
insert into @dateTable 
    select '2017-12-31', '2018-01-02' union
    select '2017-01-03', '2018-01-02' union 
    select '2017-01-02', '2018-01-02' union
    select '2017-01-01', '2018-01-02' union
    select '2016-12-01', '2018-01-02' union
    select '2016-01-03', '2018-01-02' union
    select '2016-01-02', '2018-01-02' union
    select '2016-01-01', '2018-01-02' 
select date1, date2, 
        case when ((DATEPART(year, date1) < DATEPART(year, date2)) and 
                    ((DATEPART(month, date1) <= DATEPART(month, date2)) and 
(DATEPART(day, date1) <= DATEPART(day, date2)) ))
                    then DATEDIFF(year, date1, date2)
            when (DATEPART(year, date1) < DATEPART(year, date2))
                    then DATEDIFF(year, date1, date2) - 1
            when (DATEPART(year, date1) = DATEPART(year, date2))
                    then 0
        end [YearsOfService]
from @dateTable

date1                   date2                   YearsOfService
----------------------- ----------------------- --------------
2016-01-01 00:00:00.000 2018-01-02 00:00:00.000 2
2016-01-02 00:00:00.000 2018-01-02 00:00:00.000 2
2016-01-03 00:00:00.000 2018-01-02 00:00:00.000 1
2016-12-01 00:00:00.000 2018-01-02 00:00:00.000 1
2017-01-01 00:00:00.000 2018-01-02 00:00:00.000 1
2017-01-02 00:00:00.000 2018-01-02 00:00:00.000 1
2017-01-03 00:00:00.000 2018-01-02 00:00:00.000 0
2017-12-31 00:00:00.000 2018-01-02 00:00:00.000 0
declare@dateTable表(date1 datetime,date2 datetime)
插入到@dateTable中
选择“2017-12-31”、“2018-01-02”工会
选择“2017-01-03”、“2018-01-02”工会
选择“2017-01-02”、“2018-01-02”联合体
选择“2017-01-01”、“2018-01-02”联合体
选择“2016-12-01”、“2018-01-02”联合体
选择“2016-01-03”、“2018-01-02”联合体
选择“2016-01-02”、“2018-01-02”联合体
选择“2016-01-01”、“2018-01-02”
选择日期1,日期2,
(日期部分(年,日期1)<日期部分(年,日期2))和

((DATEPART(month,date1)您想要年差,但当未来日期的“年中日期”小于过去日期的“年中日期”时,将其减少1。如下所示:

SELECT *
,DATEDIFF(YEAR, [Begin_date], [End_Date])
 + CASE WHEN CAST(DATENAME(DAYOFYEAR, [End_Date]) AS INT)
          >= CAST(DATENAME(DAYOFYEAR, [Begin_date]) AS INT)
   THEN 0 ELSE -1 END
 AS 'Age in Years'
from [myTable];

如果您只需要一个有效数字,请尝试DATEDIFF(dd..然后除以365。不过,这在闰年中不会起作用。这只会使它通过1+-.1进行计算。正确吗?当我这样做时,它仍然是四舍五入的。尝试除以365.0而不是365,这将停止隐式转换为整数。这就成功了。谢谢。列仍然是整数rounding@JeffOrris-…那么它是否回答了您的问题不是吗?你的接受意味着一件事,但你的评论意味着另一件事。它可能会被截断,因为数学只作为整数执行(即,它应该是
/12.0
,以强制浮点数学,就像在对你问题的评论中).@Fumbles-您需要将相关代码添加到此类答案中,否则此答案应关闭。@FumblesWithCode它回答了我的问题。
DATEDIFF(月、@start、@end)存在舍入问题/12.0
当两个日期都在同一个月,并且开始日期更大时。谢谢@Shago,我已经为此在办公桌上敲了一个小时的头。我想这就是真正发生的事情。跨越边界与舍入不同,尽管它可能产生看起来像舍入的结果。
         select datediff(day,@d1 ,@d2) / 365.2425
         -- = 115 years => wrong!
   declare @d1 datetime set @d1 = '1900-01-01'

   while(@d1 < '2016-01-01')
   begin
    declare @d2 datetime set @d2 = '2016-04-01'

    while(@d2 >= '1900-01-01')
    begin
        if (@d1 <= @d2 and dateadd(YEAR,     cast(datediff(day,@d1,@d2) / 365.2425 as int)      , @d1) > @d2)
        begin
            select 'not a year!!', @d1, @d2, cast(datediff(day,@d1,@d2) / 365.2425 as int)
        end

        set @d2 = dateadd(day,-1,@d2)
    end

    set @d1 = dateadd(day,1,@d1)
  end
declare @dateTable table (date1 datetime, date2 datetime)
insert into @dateTable 
    select '2017-12-31', '2018-01-02' union
    select '2017-01-03', '2018-01-02' union 
    select '2017-01-02', '2018-01-02' union
    select '2017-01-01', '2018-01-02' union
    select '2016-12-01', '2018-01-02' union
    select '2016-01-03', '2018-01-02' union
    select '2016-01-02', '2018-01-02' union
    select '2016-01-01', '2018-01-02' 
select date1, date2, 
        case when ((DATEPART(year, date1) < DATEPART(year, date2)) and 
                    ((DATEPART(month, date1) <= DATEPART(month, date2)) and 
(DATEPART(day, date1) <= DATEPART(day, date2)) ))
                    then DATEDIFF(year, date1, date2)
            when (DATEPART(year, date1) < DATEPART(year, date2))
                    then DATEDIFF(year, date1, date2) - 1
            when (DATEPART(year, date1) = DATEPART(year, date2))
                    then 0
        end [YearsOfService]
from @dateTable

date1                   date2                   YearsOfService
----------------------- ----------------------- --------------
2016-01-01 00:00:00.000 2018-01-02 00:00:00.000 2
2016-01-02 00:00:00.000 2018-01-02 00:00:00.000 2
2016-01-03 00:00:00.000 2018-01-02 00:00:00.000 1
2016-12-01 00:00:00.000 2018-01-02 00:00:00.000 1
2017-01-01 00:00:00.000 2018-01-02 00:00:00.000 1
2017-01-02 00:00:00.000 2018-01-02 00:00:00.000 1
2017-01-03 00:00:00.000 2018-01-02 00:00:00.000 0
2017-12-31 00:00:00.000 2018-01-02 00:00:00.000 0
SELECT *
,DATEDIFF(YEAR, [Begin_date], [End_Date])
 + CASE WHEN CAST(DATENAME(DAYOFYEAR, [End_Date]) AS INT)
          >= CAST(DATENAME(DAYOFYEAR, [Begin_date]) AS INT)
   THEN 0 ELSE -1 END
 AS 'Age in Years'
from [myTable];