web-dev-qa-db-fra.com

Calculer la différence de date exacte en années en utilisant SQL

Je reçois des rapports dans lesquels les données sont ETL dans la base de données automatiquement. J'extrais et transformais certaines de ces données pour les charger ailleurs. Une chose que je dois faire est une DATEDIFF, mais l'année doit être exacte (c'est-à-dire 4,6 ans au lieu d'être arrondie à cinq ans.

Ce qui suit est mon script:

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

La colonne 'Age_In_Years' est en cours d’arrondissement. Comment puis-je obtenir la date exacte en années?

8
Squ1rr3lz

Avez-vous essayé d’obtenir la différence en mois et de calculer les années de cette façon? Par exemple, 30 mois/12 équivaudrait à 2,5 ans.

Edit: Cette requête SQL contient plusieurs approches pour calculer la différence de date:

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

Tout datediff() do calcule le nombre de limites de période franchies entre les dates to. Par exemple

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

renvoie 1.

Vous obtiendrez un résultat plus précis si vous calculez la différence entre les deux dates en jours et que vous divisez par la longueur moyenne d'une année civile en jours sur une période de 400 ans (365.2425):

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

Par exemple,

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

return 14.29461248 - arrondissez-le simplement à la précision souhaitée.

12
Nicholas Carey

Je pense que la division par 365.2425 n’est pas un bon moyen de le faire. Aucune division ne peut y parvenir avec une précision absolue (l’utilisation de 365.25 pose également des problèmes).

Je sais que le script suivant calcule une différence de date précise (bien que ce ne soit peut-être pas le moyen le plus rapide):

        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

En comparaison:

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

Vous pourriez peut-être calculer de petites plages avec la division, mais pourquoi tenter le coup ??

Le script suivant peut aider à tester les fonctions yeardiff (il suffit de permuter le cast (dateiff (jour, @ d1, @ d2)/365.2425 en tant qu'int) en fonction de la fonction): 

   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
2
Dmitri Rechetilov

J'ai trouvé une meilleure solution. Cela suppose que la première date est inférieure ou égale à la deuxième date.

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