web-dev-qa-db-ja.com

SQLを使用して年単位の正確な日付の差を計算する

データがETLであるレポートを自動的にDBに受け取ります。そのデータの一部を抽出して変換し、別の場所にロードします。私がしなければならないことの1つはDATEDIFFですが、年は正確でなければなりません(つまり、5年に切り上げる代わりに4.6年)。

以下は私のスクリプトです:

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

'Age_In_Years'列は丸められています。正確な日付を年単位で取得するにはどうすればよいですか?

8
Squ1rr3lz

代わりに月単位の差を取得してから、そのように年数を計算してみましたか?たとえば、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
10
FumblesWithCode

datediff()が行うことは、日付と日付の間で交差する期間境界の数を計算することだけです。例えば

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

1を返します。

2つの日付の差を日数で計算し、暦年の平均の長さ(日数)を400年のスパン(365.2425)で割ると、より正確な結果が得られます。

datediff(day,{start-date},{end-date},) / 365.2425

例えば、

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

戻り値14.29461248 —必要な精度に丸めるだけです。

14
Nicholas Carey

365.2425による除算は、これを行う良い方法ではないと思います。部門はこれを完全に正確に行うことはできません(365.25を使用することにも問題があります)。

次のスクリプトは正確な日付の差を計算することを知っています(最も迅速な方法ではないかもしれません)。

        declare @d1 datetime ,@d2 datetime
        --set your dates eg: 
        select @d1 = '1901-03-02'
        select @d2 = '2016-03-01'

        select DATEDIFF(yy, @d1, @d2) -
            CASE WHEN MONTH(@d2) < MONTH(@d1) THEN 1
                 WHEN MONTH(@d2) > MONTH(@d1) THEN 0
                 WHEN DAY(@d2) < DAY(@d1) THEN 1
                 ELSE 0 END

         -- = 114 years

比較のために:

         select datediff(day,@d1 ,@d2) / 365.2425
         -- = 115 years => wrong!

あなたは除算で小さな範囲を計算することができるかもしれませんが、なぜチャンスを取るのですか?

次のスクリプトは、yeardiff関数をテストするのに役立ちます(関数が何であるかにキャスト(datediff(day、@ d1、@ d2)/ 365.2425 intとして入れ替えるだけです)。)

   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
4

より良い解決策を見つけました。これは、最初の日付が2番目の日付以下であることを前提としています。

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
0
K'Dubb