【问题标题】:SQL Server 2012 : DATEDIFF function in terms of years, days and monthsSQL Server 2012:以年、日和月表示的 DATEDIFF 函数
【发布时间】:2018-11-17 14:18:44
【问题描述】:

我在 SQL Server 2012 中使用 DATEDIFF 函数。我有两个日期,2015-01-01current_date。我需要DATEDIFF 在这两个日期之间的年、月和日。以下是我的陈述:

SELECT DATEDIFF(YY, '2015-01-01', GETDATE()) AS 'Years'
SELECT DATEDIFF(MM, '2015-01-01', GETDATE()) AS 'Months'
SELECT DATEDIFF(DD, '2015-01-01', GETDATE()) AS 'Days'

我得到的结果是:年 = 3,月 = 46,天 = 1416

为什么要在几个月和几天内增加一年?

【问题讨论】:

  • SELECT DATEDIFF(YY, '2015-12-31', '2016-01-01') 是一年。

标签: sql sql-server datediff date-difference


【解决方案1】:

DATEDIFF() 测量两个日期/时间值之间的时间边界数。

因此,使用year,它测量一年翻转的次数(即新的一年开始)。

使用month,它测量月份翻转的次数(即新月份的开始)。

使用day,它测量一天翻转的次数(即新的一天开始)。

这些都是相互独立的。

我建议您不要尝试获取以年/月/日为单位的值。这是一个难题。 2 月 28 日至 3 月 31 日之间的月数/天数是多少?在 1 月 31 日到 2 月 28 日之间?在 1 月 31 日到 3 月 31 日之间?他们没有加起来,这使这成为一个难题。

【讨论】:

    【解决方案2】:

    也许有点矫枉过正,但如果你对 TVF 持开放态度

    示例

    Declare @YourTable table (SomeDate date)
    Insert Into @YourTable values 
    ('2015-01-01')
    
    Select A.SomeDate
          ,B.*
     From  @YourTable A
     Cross Apply [dbo].[tvf-Date-Elapsed](SomeDate,GetDate()) B
    

    退货

    SomeDate    Years   Months  Days    Hours   Minutes Seconds
    2015-01-01  3       10      16      8       22      40
    

    感兴趣的功能

    CREATE FUNCTION [dbo].[tvf-Date-Elapsed] (@D1 DateTime,@D2 DateTime)
    Returns Table
    Return (
        with cteBN(N)   as (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
             cteRN(R)   as (Select Row_Number() Over (Order By (Select NULL))-1 From cteBN a,cteBN b,cteBN c),
             cteYY(N,D) as (Select Max(R),Max(DateAdd(YY,R,@D1))From cteRN R Where DateAdd(YY,R,@D1)<=@D2),
             cteMM(N,D) as (Select Max(R),Max(DateAdd(MM,R,D))  From (Select Top 12 R From cteRN Order By 1) R, cteYY P Where DateAdd(MM,R,D)<=@D2),
             cteDD(N,D) as (Select Max(R),Max(DateAdd(DD,R,D))  From (Select Top 31 R From cteRN Order By 1) R, cteMM P Where DateAdd(DD,R,D)<=@D2),
             cteHH(N,D) as (Select Max(R),Max(DateAdd(HH,R,D))  From (Select Top 24 R From cteRN Order By 1) R, cteDD P Where DateAdd(HH,R,D)<=@D2),
             cteMI(N,D) as (Select Max(R),Max(DateAdd(MI,R,D))  From (Select Top 60 R From cteRN Order By 1) R, cteHH P Where DateAdd(MI,R,D)<=@D2),
             cteSS(N,D) as (Select Max(R),Max(DateAdd(SS,R,D))  From (Select Top 60 R From cteRN Order By 1) R, cteMI P Where DateAdd(SS,R,D)<=@D2)
    
        Select [Years]   = cteYY.N
              ,[Months]  = cteMM.N
              ,[Days]    = cteDD.N
              ,[Hours]   = cteHH.N
              ,[Minutes] = cteMI.N
              ,[Seconds] = cteSS.N
              --,[Elapsed] = Format(cteYY.N,'0000')+':'+Format(cteMM.N,'00')+':'+Format(cteDD.N,'00')+' '+Format(cteHH.N,'00')+':'+Format(cteMI.N,'00')+':'+Format(cteSS.N,'00')
         From  cteYY,cteMM,cteDD,cteHH,cteMI,cteSS
    )
    --Max 1000 years
    --Select * from [dbo].[tvf-Date-Elapsed] ('1991-09-12 21:00:00.000',GetDate())
    --Select * from [dbo].[tvf-Date-Elapsed] ('2017-01-01 20:30:15','2018-02-05 22:58:35')
    

    【讨论】:

    • 这很好用。我不同意返回的一个结果是在2016022920170328 之间;返回值为 1 年 1 个月。就我个人而言,我建议它是 1 年 28 天(因为天数的值低于开始),但是,闰年总是“有趣”的麻烦制造者。 :)
    • @Larnu 在您的场景中,您跨越的是闰年,所以它是 1 个月。如果您要尝试 20160229 - 20160328,您将获得 28 天。至少我是这样计算的。也就是说,我很欣赏这个挑战:)
    • 我同意,20160229-20160328 是 28 天,但这就是为什么我也觉得 20160229 -20170328 是 1 年 28 天(不是 1 年 1 个月)。同样,我不会说20160229-20170329 是 1 年 1 天,而是 1 年 1 个月。但是,就像我说的,闰年总是“麻烦制造者”。
    • @larnu 我认为唯一合理的标准是结果应该可以在DATEADD() 中使用,以从@D1 变为@D2。但是,那么,添加间隔的顺序很重要; DATEADD(DAY, 29, DATEADD(YEAR, 1, '2015-01-31')) != DATEADD(YEAR, 1, DATEADD(DAY, 29, '2015-01-31')) (首先添加最大间隔对我来说很有意义)。然而,同样地,多个合法答案是可能的; DATEADD(DAY, 28, DATEADD(YEAR, 1, '2016-02-29')) == DATEADD(MONTH, 1, DATEADD(YEAR, 1, '2016-02-29'))
    • @larnu(和约翰):这次尝试的 cmets? dbfiddle.uk/…
    【解决方案3】:

    我对@JohnCappelletti 的启发性回答的看法

    CREATE FUNCTION [dbo].[tvf_interval_parts] (@start DateTime, @cease DateTime)
    Returns Table
    RETURN
      SELECT
        *
      FROM
      (
        SELECT
          months / 12   AS years,
          months % 12   AS months
        FROM
        (
          SELECT
            CASE WHEN DATEADD(MONTH, months, @start) > @cease                           THEN months - 1
                 WHEN DATEADD(MONTH, months, @start) = DATEADD(MONTH, months, @start-1) THEN months - 1
                                                                                        ELSE months     END   AS months
          FROM
          (
            SELECT DATEDIFF(MONTH, @start, @cease) AS months
          )
            provisional
        )
          adjusted
      )
        interim
      CROSS APPLY
      (
          SELECT
            milliseconds                         / (24 * 60 * 60 * 1000)  AS days,
            milliseconds % (24 * 60 * 60 * 1000) / (     60 * 60 * 1000)  AS hours,
            milliseconds % (     60 * 60 * 1000) / (          60 * 1000)  AS minutes,
            milliseconds % (          60 * 1000) / (               1000)  AS seconds,
            milliseconds % (               1000)                          AS milliseconds
          FROM
          (
            SELECT DATEDIFF_BIG(millisecond, DATEADD(MONTH, 12 * years + months, @start), @cease) AS milliseconds
          )
            provisional
      )
        remainder
    

    这里的用法示例:https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=47b2d437c4cea78d182ce0f63772ef38

    【讨论】:

    • 只有真正痴迷到毫秒的人 :) +1
    猜你喜欢
    • 2014-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-11
    • 1970-01-01
    • 2015-02-10
    • 2019-04-10
    相关资源
    最近更新 更多