【问题标题】:How to use DATEDIFF to return year, month and day?如何使用 DATEDIFF 返回年月日?
【发布时间】:2010-12-05 05:20:15
【问题描述】:

如何使用DATEDIFF 来返回SQL Server 2005 中两个日期的年、月、日之间的差值

DATEDIFF (date , date)

如何得出结果:2 年 3 个月 10 天

谁能完成这个t-sql

ALTER FUNCTION [dbo].[gatYMD](@dstart VARCHAR(50), @dend VARCHAR(50))
RETURNS VARCHAR(50) AS
BEGIN
    DECLARE @yy INT
    DECLARE @mm INT
    DECLARE @getmm INT
    DECLARE @dd INT

    SET @yy = DATEDIFF(yy, @dstart, @dend)
    SET @mm = DATEDIFF(mm, @dstart, @dend)
    SET @dd = DATEDIFF(dd, @dstart, @dend)
    SET @getmm = ABS(DATEDIFF(mm, DATEADD(yy, @yy, @dstart), @dend))

    RETURN (
        Convert(varchar(10),@yy) + 'year' + Convert(varchar(10),@mm) + 'month'  + Convert(varchar(10),@dd) + 'day'
        )
END

【问题讨论】:

  • 请注意,一般来说,这个问题是不明确的。考虑最近记忆中的两个日期:2009-02-25 和 2009-03-28。我怀疑预期的答案是 0 年 1 个月 3 天。 2009-02-25 和 2009-03-05 呢? 2009 年 3 月 25 日和 2009 年 4 月 5 日? 2009 年 2 月 25 日和 2009 年 4 月 5 日?其中第一个可能应该是 0y、0m、8d;第二个可能是 0y、0m、11d;我不确定第三个 - 可能是 0y、1m、11d。 2000-02-29 和 2003-01-31 呢? 2000-02-29 和 2003-02-28?

标签: sql sql-server sql-server-2005


【解决方案1】:

这是我对 Eric 函数的解决方案:

DECLARE @getmm INT
DECLARE @getdd INT

SET @yy = DATEDIFF(yy, @dstart, @dend)
SET @mm = DATEDIFF(mm, @dstart, @dend)
SET @dd = DATEDIFF(dd, @dstart, @dend)
SET @getmm = ABS(DATEDIFF(mm, DATEADD(yy, @yy, @dstart), @dend))
SET @getdd = ABS(DATEDIFF(dd, DATEADD(mm, DATEDIFF(mm, DATEADD(yy, @yy, @dstart), @dend), DATEADD(yy, @yy, @dstart)), @dend))

RETURN (
  Convert(varchar(10),@yy) + 'year' + Convert(varchar(10),@getmm) + 'month'  + Convert(varchar(10),@getdd) + 'day'
)

如果开始日期晚于结束日期,请使用 ABS 处理。


这个:

WITH ex_table AS (
  SELECT '2007-01-01' 'birthdatetime',
         '2009-03-29' 'visitdatetime')
SELECT CAST(DATEDIFF(yy, t.birthdatetime, t.visitdatetime) AS varchar(4)) +' year '+
       CAST(DATEDIFF(mm, DATEADD(yy, DATEDIFF(yy, t.birthdatetime, t.visitdatetime), t.birthdatetime), t.visitdatetime) AS varchar(2)) +' month '+
       CAST(DATEDIFF(dd, DATEADD(mm, DATEDIFF(mm, DATEADD(yy, DATEDIFF(yy, t.birthdatetime, t.visitdatetime), t.birthdatetime), t.visitdatetime), DATEADD(yy, DATEDIFF(yy, t.birthdatetime, t.visitdatetime), t.birthdatetime)), t.visitdatetime) AS varchar(2)) +' day' AS result
  FROM ex_table t

..或用于 SQL Server 2000 及更早版本的非 CTE:

SELECT CAST(DATEDIFF(yy, t.birthdatetime, t.visitdatetime) AS varchar(4)) +' year '+
       CAST(DATEDIFF(mm, DATEADD(yy, DATEDIFF(yy, t.birthdatetime, t.visitdatetime), t.birthdatetime), t.visitdatetime) AS varchar(2)) +' month '+
       CAST(DATEDIFF(dd, DATEADD(mm, DATEDIFF(mm, DATEADD(yy, DATEDIFF(yy, t.birthdatetime, t.visitdatetime), t.birthdatetime), t.visitdatetime), DATEADD(yy, DATEDIFF(yy, t.birthdatetime, t.visitdatetime), t.birthdatetime)), t.visitdatetime) AS varchar(2)) +' day' AS result
  FROM (SELECT '2007-01-01' 'birthdatetime',
         '2009-03-29' 'visitdatetime') t

...将返回:

result
----------------------
2 year 2 month 28 day

参考:DATEDIFF

【讨论】:

  • DATEDIFF(dd, t.start_date, t.end_date) 都返回日期?不减?
  • @monkey_boys:DATEDIFF 根据提供的日期的 datepart 值将差异返回为 int。 IE: yy 表示年份,因此 DATEDIFF(yy, t.start_date, t.end_date) 将返回所提供日期之间的年数。你可以随时在你的系统上测试它以确保。
  • Msg 245,Level 16,State 1,Line 1 将 varchar 值“year”转换为数据类型 int 时转换失败。
  • Convert(varchar(10),DATEDIFF(yy,new.BIRTHDATETIME,new.VISITDATETIME)) +'year ' + Convert(varchar(10),DATEDIFF(mm,new.BIRTHDATETIME,new. VISITDATETIME)) +'月' + Convert(varchar(10),DATEDIFF(dd,new.BIRTHDATETIME,new.VISITDATETIME)) +'day' AS 年龄
  • 不适用于闰年 - 尝试 2011-09-30 到 2011-09-29 返回“1 年 0 月 -1 天”
【解决方案2】:

查看此页面...http://www.sqlteam.com/article/datediff-function-demystified

创建这个函数:

CREATE FUNCTION dbo.fnYearsApart
(
        @FromDate DATETIME,
        @ToDate DATETIME
)
RETURNS INT
AS
BEGIN
        RETURN  CASE
                       WHEN @FromDate > @ToDate THEN NULL
                       WHEN DATEPART(day, @FromDate) > DATEPART(day, @ToDate) THEN DATEDIFF(month, @FromDate, @ToDate) - 1
                       ELSE DATEDIFF(month, @FromDate, @ToDate)
               END / 12
END

CREATE FUNCTION dbo.fnMonthsApart
(
        @FromDate DATETIME,
        @ToDate DATETIME
)
RETURNS INT
AS
BEGIN
        RETURN  CASE
                       WHEN @FromDate > @ToDate THEN NULL
                       WHEN DATEPART(day, @FromDate) > DATEPART(day, @ToDate) THEN DATEDIFF(month, @FromDate, @ToDate) - 1
                       ELSE DATEDIFF(month, @FromDate, @ToDate)
               END
END

最后

ALTER FUNCTION [dbo].[gatYMD](@dstart VARCHAR(50), @dend VARCHAR(50))
RETURNS VARCHAR(50) AS
BEGIN
    DECLARE @yy INT
    DECLARE @mm INT
    DECLARE @dd INT
    DECLARE @getmm INT
    DECLARE @getdd INT

    SET @yy = dbo.fnYearsApart(@dstart, @dend)  --DATEDIFF(yy, @dstart, @dend)
    SET @mm = dbo.fnMonthsApart(@dstart, @dend) --DATEDIFF(mm, @dstart, @dend)
    SET @dd = DATEDIFF(dd, @dstart, @dend)
    SET @getmm = ABS(DATEDIFF(mm, DATEADD(yy, @yy, @dstart), @dend))
    SET @getdd = ABS(DATEDIFF(dd, DATEADD(mm, DATEDIFF(mm, DATEADD(yy, @yy, @dstart), @dend), DATEADD(yy, @yy, @dstart)), @dend))

    RETURN (
      Convert(varchar(10),@yy) + ' años, ' + Convert(varchar(10),@getmm) + ' meses, '  + Convert(varchar(10),@getdd) + ' días'
    )
END

太棒了!

【讨论】:

    【解决方案3】:

    它适用于某些情况,但是当您从 2011-01-13(日期终止)中减去诸如 2007-10-15(DateHired)之类的日期时,它会给您一个负数,而腹肌不在它周围,而是放周围的 abs 也不能解决它,因为年份和月份的数字不正确。

    【讨论】:

      【解决方案4】:

      修改后的函数

      ALTER FUNCTION [dbo].[gatYMD](@dstart VARCHAR(50), @dend VARCHAR(50))
      RETURNS VARCHAR(50) AS
      BEGIN
          DECLARE @yy INT
          DECLARE @mm INT
          DECLARE @getdd INT
          DECLARE @dd INT
      
          SET @yy = DATEDIFF(yy, @dstart, @dend)
          SET @mm = DATEDIFF(mm, @dstart, @dend) - (12 * @yy)
          SET @dd = ABS(DATEDIFF(dd, DATEADD(mm, DATEDIFF(mm, DATEADD(yy, @yy, @dstart), @dend), DATEADD(yy, @yy, @dstart)), @dend))
      
      
          Return (Convert(varchar(10),@yy) + ' year ' + Convert(varchar(10),@mm) + ' month '  + Convert(varchar(10),@dd) + ' day ')
      
      END
      

      【讨论】:

      • select dbo.gatYMD('2018-10-01','2019-01-01') 输出为 1 年 -9 月 0 日
      【解决方案5】:

      创建这个函数,它会给出精确的日期差异,比如年月日

          Create function get_Exact_Date_diff(@date smalldatetime,@date2 smalldatetime)
       returns varchar(50)
      
          as
      
          begin
      
          declare @date3 smalldatetime
      
          Declare @month int,@year int,@day int
      
           if @date>@date2
           begin
           set @date3=@date2
           set @date2=@date
           set @date=@date3
           end
      
      
      
          SELECT @month=datediff (MONTH,@date,@date2)
      
          if dateadd(month,@month,@date) >@date2
          begin
          set @month=@month-1
          end
          set @day=DATEDIFF(day,dateadd(month,@month,@date),@date2)
      
          set @year=@month/12
          set @month=@month % 12
      
          return (case when @year=0 then '' when @year=1 then convert(varchar(50),@year ) + ' year ' when @year>1 then convert(varchar(50),@year ) + ' years ' end)
          + (case when @month=0 then '' when @month=1 then convert(varchar(50),@month ) + ' month ' when @month>1 then convert(varchar(50),@month ) + ' months ' end)
          + (case when @day=0 then '' when @day=1 then convert(varchar(50),@day ) + ' day ' when @day>1 then convert(varchar(50),@day ) + ' days ' end)
      
          end
      

      【讨论】:

      • 这给出了日期 2015-01-24 和 2017-10-17 之间的正确 DURATION,而其他函数似乎没有。他们都给了 7 天,这是 24 和 17 之间的差异..错误! ...非常感谢:)
      【解决方案6】:
      Using ParseName
      
      DECLARE
        @ReportBeginDate DATE
      
      SET @ReportBeginDate='2015-01-01';
      
      IF OBJECT_ID('TEMPDB..#tmp_ymd') IS NOT NULL
      BEGIN
      DROP TABLE #tmp_ymd;
      END;
      
      select
      cast(cast(datediff(mm,@ReportBeginDate,getdate()) as decimal (10,2))/12 as decimal(10,2)) as YearMonthDec
      ,cast(datediff(dd,@ReportBeginDate,getdate()) as decimal (10,2)) as DayDec
      into #tmp_ymd
      
      select
      YearMonthDec
      ,cast(parsename(YearMonthDec,2) as decimal (10,0)) as yearnum
      ,cast(cast(parsename(YearMonthDec,1) as decimal (10,0))/100*(12) as numeric) as monthnum
      ,case when YearMonthDec>=1 then datediff(dd,CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(getdate())-1),getdate()),101),getdate()) else DayDec end as daynum
      
      from #tmp_ymd
      

      【讨论】:

      • 您正在回答一个已有 7 年历史的问题,该问题已有多个答案。您可能需要编辑您的问题,以包含一些注释,解释此代码的作用,以及为什么它改进了已有的内容。
      【解决方案7】:

      我知道这里已经有一些答案,但我想我会添加我想出的内容,因为(至少对我而言)它似乎很容易理解:

      CREATE FUNCTION dbo.fn_DateDiff_YMDMHS
      (   
          @Startdate as datetime2(0),
          @Enddate as datetime2(0)
      )
      RETURNS TABLE 
      AS
      RETURN 
      (
          select 
              TotalYears [Years],
              datediff(month, dateadd(Year, TotalYears, @Startdate), @Enddate) Months,
              datediff(day, dateadd(month, TotalMonths, @Startdate), @Enddate) [Days],
              datediff(hour, dateadd(day, TotalDays, @Startdate), @Enddate) [Hours],
              datediff(minute, dateadd(hour, TotalHours, @Startdate), @Enddate) [Minutes],
              datediff(second, dateadd(minute, TotalMinutes, @Startdate), @Enddate) [Seconds]
          from (
          select 
              datediff(SECOND, @Startdate, @Enddate) TotalSeconds,
              datediff(minute, @Startdate, @Enddate) TotalMinutes,
              datediff(hour, @Startdate, @Enddate) TotalHours,
              datediff(day, @Startdate, @Enddate) TotalDays,
              datediff(month, @Startdate, @Enddate) TotalMonths,
              datediff(year, @Startdate, @Enddate) TotalYears) DateDiffs
          )
      

      然后当你打电话时:

      select * from dbo.fn_DateDiff_YMDMHS('1900-01-01 00:00:00', '1910-10-05 03:01:02')
      

      你会得到这个返回:

      Years   Months  Days    Hours   Minutes Seconds
      10      9       4       3       1       2
      

      显然,您可以将其更改为格式化输出并使用标量变量,但我将把它留给您 :-)

      编辑:

      我最终还需要执行 time ago 函数来返回“5 年零 2 天前”之类的格式

      CREATE FUNCTION fn_DateDiff_YMDMHS_String
      (
          @StartDate datetime2(0),
          @EndDate datetime2(0),
          @OutputYears bit = 1,
          @OutputMonths bit = 1,
          @OutputDays bit = 1,
          @OutputHours bit = 0,
          @OutputMinutes bit = 0,
          @OutputSeconds bit = 0,
          @OutputSuffix bit = 0
      )
      RETURNS varchar(256)
      AS
      BEGIN
          DECLARE @Output varchar(256) = ''
          declare @Years int, @Months int, @Days int, @Hours int, @Minutes int, @Seconds int
      
          select 
              @Years = case when @OutputYears = 1 then Years else 0 end,
              @Months = case when @OutputMonths = 1 then Months else 0 end,
              @Days = case when @OutputDays = 1 then Days else 0 end,
              @Hours = case when @OutputHours = 1 then Hours else 0 end,
              @Minutes = case when @OutputMinutes = 1 then Minutes else 0 end,
              @Seconds = case when @OutputSeconds = 1 then Seconds else 0 end 
          from dbo.fn_DateDiff_YMDMHS(@StartDate, @EndDate)
      
          declare @and varchar(5) = ''
          if @OutputYears = 1 and @Years > 0 
          begin
              set @Output = @Output + cast(@Years as varchar(4)) + ' year' 
              if @Years > 1 set @Output = @Output + 's ' else set @Output = @Output + ' '
              if @Months > 0 and @Days + @Hours + @Minutes + @Seconds = 0 set @and = 'and '
          end
          if @OutputMonths = 1 and @Months > 0 
          begin
              set @Output = @Output + @and + cast(@Months as varchar(2)) + ' month'
              if @Months > 1 set @Output = @Output + 's ' else set @Output = @Output + ' '
              if @Days > 0 and @Hours + @Minutes + @Seconds = 0 set @and = 'and '
          end
          if @OutputDays = 1 and @Days > 0 
          begin
              set @Output = @Output + @and + cast(@Days as varchar(2)) + ' day'
              if @Days > 1 set @Output = @Output + 's ' else set @Output = @Output + ' '
              if @Hours > 0 and @Minutes + @Seconds = 0 set @and = 'and '
          end
          if @OutputHours = 1 and @Hours > 0 
          begin
              set @Output = @Output + @and + cast(@Hours as varchar(2)) + ' hour'
              if @Hours > 1 set @Output = @Output + 's ' else set @Output = @Output + ' '
              if @Minutes > 0 and @Seconds = 0 set @and = 'and '
          end
          if @OutputMinutes = 1 and @Minutes > 0 
          begin
              set @Output = @Output + @and + cast(@Minutes as varchar(2)) + ' minute'
              if @Minutes > 1 set @Output = @Output + 's ' else set @Output = @Output + ' '
              if @Seconds > 0 set @and = 'and '
          end
          if @OutputSeconds = 1 and @Seconds > 0 
          begin
              set @Output = @Output + @and + cast(@Seconds as varchar(2)) + ' second'
              if @Seconds > 1 set @Output = @Output + 's ' else set @Output = @Output + ' '
          end
      
          if @OutputSuffix = 1
          begin
              if @StartDate < @EndDate
              begin
                  set @Output = @Output + 'ago'
              end
              else
              begin
                  set @Output = 'in ' + @Output
              end
          end
      
          RETURN @Output
      
      END
      

      这里有 2 个例子

      select dbo.fn_DateDiff_YMDMHS_String('2000-01-01 00:00:00', '2007-09-19 14:39:53', 1, 1, 1, 1,1,1, 1)
      7 years 8 months 18 days 8 hours 39 minutes and 53 seconds ago
      

      select dbo.fn_DateDiff_YMDMHS_String('2000-01-01 00:00:00', '2007-09-19 14:39:53', 1, 1, 1, 0,0,0, 1)
      7 years 8 months and 18 days ago
      

      无论如何,我希望这对将来的某个人有用,因为我在搜索时找不到很多类似的东西(尽管可能只是那些日子之一)我对改进持开放态度,因为我知道我是并不总是最紧凑或最快的代码程序员:-)

      问候

      利亚姆

      【讨论】:

      • 这个答案是最接近的,但是 select * from [dbo].[fn_DateDiff_YMDMHS]('2017-07-10 18:07:02.857', '2017-08-09 15:11:51.963 ') 结果:1 个月 -1 天 -3 小时
      【解决方案8】:
      CREATE FUNCTION [dbo].[FindDateDiff](@Date1 date,@Date2 date, @IncludeTheEnDate bit)
      RETURNS TABLE 
      AS
      RETURN 
      (
          SELECT
              CALC.Years,CALC.Months,D.Days,
              Duration = RTRIM(Case When CALC.Years > 0 Then CONCAT(CALC.Years, ' year(s) ') Else '' End
                             + Case When CALC.Months > 0 Then CONCAT(CALC.Months, ' month(s) ') Else '' End
                             + Case When D.Days > 0 OR (CALC.Years=0 AND CALC.Months=0) Then CONCAT(D.Days, ' day(s)') Else '' End)
          FROM (VALUES(IIF(@Date1<@Date2,@Date1,@Date2),DATEADD(DAY, IIF(@IncludeTheEnDate=0,0,1), IIF(@Date1<@Date2,@Date2,@Date1)))) T(StartDate, EndDate)
          CROSS APPLY(Select
              TempEndYear = Case When ISDATE(CONCAT(YEAR(T.EndDate), FORMAT(T.StartDate,'-MM-dd')))=1 Then CONCAT(YEAR(T.EndDate), FORMAT(T.StartDate,'-MM-dd'))
                              Else CONCAT(YEAR(T.EndDate),'-02-28') End
          ) TEY
          CROSS APPLY(Select EndYear = Case When TEY.TempEndYear > T.EndDate Then DATEADD(YEAR, -1, TEY.TempEndYear) Else TEY.TempEndYear End) EY
          CROSS APPLY(Select
              Years = DATEDIFF(YEAR,T.StartDate,EY.EndYear),
              Months = DATEDIFF(MONTH,EY.EndYear,T.EndDate)-IIF(DAY(EY.EndYear)>DAY(T.EndDate),1,0)
          ) CALC
          CROSS APPLY(Select Days =  DATEDIFF(DAY,DATEADD(MONTH,CALC.Months,DATEADD(YEAR,CALC.Years,T.StartDate)),T.EndDate)) D
      )
      

      示例:

      Select [From] = '2021-01-01',[To] = '2021-12-31',IncludeEndDate='Yes',* From dbo.FindDateDiff('2021-01-01','2021-12-31',1)
      Select [From] = '2021-01-01',[To] = '2021-12-31',IncludeEndDate='No',* From dbo.FindDateDiff('2021-01-01','2021-12-31',0)
      Select [From] = '2015-12-15',[To] = '2018-12-14',IncludeEndDate='Yes',* From dbo.FindDateDiff('2015-12-15','2018-12-14',1)
      Select [From] = '2015-12-15',[To] = '2018-12-14',IncludeEndDate='No',* From dbo.FindDateDiff('2015-12-15','2018-12-14',0)
      

      【讨论】:

        【解决方案9】:

        TL;DR 无需创建函数即可近似数年和数月。


        我在搜索“从 datediff 中获取年月”时发现了这个问题。我一直在寻找比上述函数创建解决方案更快(是的,可能更脏)的东西。

        我想出了以下内嵌 sql,它近似年和月。足以满足我自己的目的,即按用户当时的粗略年龄分组的事件计数。

        select cast(datediff(event_datetime, dateofbirth)/365.25 as unsigned) as years, 
               cast((mod(datediff(event_datetime, dateofbirth),365.25))/30.4375 as unsigned) as months
        from tablename
        ;
        

        【讨论】:

          猜你喜欢
          • 2020-06-11
          • 2014-10-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-11-23
          • 2013-07-23
          • 1970-01-01
          • 2020-01-08
          相关资源
          最近更新 更多