【问题标题】:Expanding Months Between Two Dates扩展两个日期之间的月份
【发布时间】:2019-08-07 15:03:31
【问题描述】:

我正在尝试在 SQL 中找到一种方法来列出每个用户两个日期之间的所有月份。该表将具有:

RecordNumber(PK), StartDate, EndDate, Type.

我找到了两个日期之间所有月份的另一个 stackoverflow 页面,但它不适用于我的用例,因为我认为它只适用于一个人。

我们的想法是创建另一个字段,导致开始日期和结束日期之间的每个月和年。

所以对于一个示例表:

Record   Startdate    End Date     Type    
1234   | 01-01-2019 | 04-01-2019 | TypeA 
5678   | 03-01-2019 | 04-01-2019 | TypeB

结果是:

Record   Startdate    End Date     Type    NewField
1234   | 01-01-2019 | 04-01-2019 | TypeA | January2019
1234   | 01-01-2019 | 04-01-2019 | TypeA | February2019
1234   | 01-01-2019 | 04-01-2019 | TypeA | March2019
1234   | 01-01-2019 | 04-01-2019 | TypeA | April2019
5678   | 03-01-2019 | 04-01-2019 | TypeB | March2019
5678   | 03-01-2019 | 04-01-2019 | TypeB | April2019

【问题讨论】:

    标签: sql sql-server date


    【解决方案1】:

    您可以使用递归 CTE:

    with cte as (
          select Record, Startdate, EndDate, Type, startdate as dte
          from t
          union all
          select Record, Startdate, EndDate, Type, dateadd(month, 1, dte) as dte
          from cte
          where dte < enddate
         )
    select Record, Startdate, EndDate, Type, datename(month, dte) + datename(year, dte)
    from cte
    order by record, dte;
    

    Here 是一个 dbfiddle。

    如果您可能有超过 100 个月的给定记录,则包括 option (maxrecursion 0)

    【讨论】:

    • 嗨,戈登,感谢您的回复。这看起来很有希望,我将插入我的字段名称等并试一试。我确实有超过 100 个,所以 maxrecursion 也是一个很大的帮助。我会回复结果。
    • 我认为它正在努力工作。我尝试使用我的完整表并在运行大约 4 小时后超时,声称 tempdb 空间不足。我只用表中的 1 条记录再次尝试,并在运行 30 多分钟后结束运行,因为无论它是否有效,完成其余记录都太长了。我认为这太过分了。我有大约 20k 条记录,每条记录的开始/结束日期为 1-2 年(12-24 个月)。感谢您的帮助,但我想我可能需要放弃这个。
    • @贾斯汀。 . .不应该花那么长时间。我修正了一个错字并包含了一个 dbfiddle.
    • 嗨,戈登,我明天会有更多时间来尝试一下,我会用一个小数据集再试一次。再次感谢您的指导。
    • Gordon 我用我的小数据集进行了尝试,效果很好。对于我的完整数据集来说,这仍然是一个很大的吸引力,但我认为这会奏效。再次感谢您的帮助!
    【解决方案2】:

    你可以创建存储过程:

    CREATE PROCEDURE dbo.GetMonthsList AS
    SELECT tb.Record,
           tb.StartDate,
           tb.EndDate,
           tb.Type,
           DATENAME(MONTH, DATEADD(MONTH, x.number, tb.StartDate)) + '-' + Convert(varchar(4), Year(DATEADD(MONTH, x.number, tb.StartDate))) AS MonthName
    FROM master.dbo.spt_values x,
         dbo.TableName tb
    WHERE x.type = 'P'
      AND x.number <= DATEDIFF(MONTH, tb.StartDate, tb.EndDate);
    
    RETURN
    

    只需将“dbo.TableName”更改为您的表即可。

    要检查您是否可以使用

    执行此存储过程

    执行 dbo.GetMonthsList

    【讨论】:

    • 您好,我不确定 master.dbo.spt_values 来自哪里。此外 type = 'P' 看起来并不适用于我。这看起来与我在“两个日期之间的所有月份”中提到的另一篇文章的答案相似,这对我的情况不起作用。谢谢
    • @Justin master..spt_values 此处使用以避免使用临时表,它是系统程序使用的表。你可以在这里找到一个很好的解释:stackoverflow.com/questions/4273978/…。在你的情况下,这个实现到底有什么不适用的?
    • 当我尝试从该表中提取时,我得到一个表不存在类型错误,我认为我无权访问主架构。
    猜你喜欢
    • 2019-03-28
    • 1970-01-01
    • 1970-01-01
    • 2012-12-24
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 2022-11-18
    相关资源
    最近更新 更多