使用 EOMONTH 系列将开始日期扩展到结束日期

Expand Start Date to End Date with Series of EOMONTHs

我有一个数据 table 包含 id,开始日期和结束日期都与之关联。

RowNo   AcNo     StartDate     EndDate
  1     R125     01/10/2017    30/09/2020
  2     R126     01/10/2017    30/09/2018
  3     R127     01/10/2017    30/09/2019
  4     R128     01/10/2017    30/09/2020

我需要扩展(即逆轴)这个 table 以允许每个 AcNo 的开始日期和结束日期(含)之间的每个 eomonth 一行。行号不重要。

AcNo    EOMONTHs
R125    Oct 17
R125    Nov 17
R125    Dec 17
R125    Jan 18
R125    Feb 18
R125    Mar 18
    ...
R128    Apr 20
R128    May 20
R128    Jun 20
R128    Jul 20
R128    Aug 20
R128    Sep 20

我可以用这样的一对公式来计算每一行,

'in F2
=IF(ROW(1:1)-1<DATEDIF(C, D, "m"), B, TEXT(,))
'in G2
=IF(ROW(1:1)-1<DATEDIF(C, D, "m"), EOMONTH(C, ROW(1:1)-1), TEXT(,))
'F2:G2 filled down

但是我有数千行 AcNos,这对单独的行执行起来很笨重。

我还使用 VBA 的 DateDiff 为各个行形成循环。

    Dim m As Long, ms As Long
    With Worksheets("Sheet2")
        .Range("F1:G1") = Array("AcNo", "EOMONTHs")
        ms = DateDiff("m", .Cells(2, "C").Value2, .Cells(2, "D").Value2)
        For m = 1 To ms + 1
            .Cells(m, "M") = .Cells(2, "B").Value2
            .Cells(m, "N").Formula = "=EOMONTH(C, " & m - 1 & ")"
        Next m
    End With

同样,这一次只会扩展一行。

我如何遍历将每个系列堆叠成一列的行?欢迎对我的公式或代码提出任何调整建议。

尝试将其作为嵌套的 For ... Next 循环使用 DateDiff 来确定月数。在数组中收集渐进值将在将它们转储回工作表之前加快执行速度。

Option Explicit

Sub eoms()
    Dim a As Long, m As Long, ms As Long, vals As Variant
    With Worksheets("Sheet2")
        .Range("F1:G1") = Array("AcNo", "EOMONTHs")
        For a = 2 To .Cells(.Rows.Count, "B").End(xlUp).Row
            ms = DateDiff("m", .Cells(a, "C").Value2, .Cells(a, "D").Value2)
            ReDim vals(1 To ms + 1, 1 To 2)
            For m = 1 To ms + 1
                vals(m, 1) = .Cells(a, "B").Value2
                vals(m, 2) = DateSerial(Year(.Cells(a, "C").Value2), _
                                        Month(.Cells(a, "C").Value2) + m, _
                                        0)
            Next m
            .Cells(.Rows.Count, "F").End(xlUp).Offset(1, 0).Resize(UBound(vals, 1), UBound(vals, 2)) = vals
        Next a
        .Range(.Cells(2, "G"), .Cells(.Rows.Count, "G").End(xlUp)).NumberFormat = "mmm yy"
    End With
End Sub

VBA 的 DateSerial 可以通过将日期设置为下个月的零来用作 EOMONTH 生成器。

请注意下图中生成的月份是系列中每个月的 EOMONTH,格式为 mmm yy 单元格编号。

只是因为你似乎在征求多种选择,这里有一个没有 VBA:

  • 用公式向右展开 table(此处使用结构化引用):

=IF(EOMONTH(Table1[@[StartDate]:[StartDate]],COLUMNS($A:A))<Table1[@[EndDate]:[EndDate]],EOMONTH(Table1[@[StartDate]:[StartDate]],COLUMNS($A:A)),"")

  • 使用 Power QueryData Get & Transform 取消透视除了前两列之外的所有内容:(在 GUI 中很容易完成,但我出于兴趣粘贴了下面的代码)

let
    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"RowNo", Int64.Type}, {"AcNo", type text}, {"StartDate", type datetime}, {"EndDate", type datetime}, {"Column1", type datetime}, {"Column2", type datetime}, {"Column3", type datetime}, {"Column4", type datetime}, {"Column5", type datetime}, {"Column6", type datetime}, {"Column7", type datetime}, {"Column8", type datetime}, {"Column9", type datetime}, {"Column10", type datetime}, {"Column11", type datetime}, {"Column12", type datetime}, {"Column13", type datetime}, {"Column14", type datetime}, {"Column15", type datetime}, {"Column16", type datetime}, {"Column17", type datetime}, {"Column18", type datetime}, {"Column19", type datetime}, {"Column20", type datetime}, {"Column21", type datetime}, {"Column22", type datetime}, {"Column23", type datetime}, {"Column24", type datetime}, {"Column25", type datetime}, {"Column26", type datetime}, {"Column27", type datetime}, {"Column28", type datetime}, {"Column29", type datetime}, {"Column30", type datetime}, {"Column31", type datetime}, {"Column32", type datetime}, {"Column33", type datetime}, {"Column34", type datetime}}),
    #"Unpivoted Other Columns" = Table.UnpivotOtherColumns(#"Changed Type", {"RowNo", "AcNo"}, "Attribute", "Value"),
    #"Removed Columns" = Table.RemoveColumns(#"Unpivoted Other Columns",{"Attribute"}),
    #"Renamed Columns" = Table.RenameColumns(#"Removed Columns",{{"Value", "EOM Date"}}),
    #"Changed Type1" = Table.TransformColumnTypes(#"Renamed Columns",{{"EOM Date", type date}})
in
    #"Changed Type1"
  • 按 AcNo 然后按日期对结果进行排序:

请注意,以这种方式完成后,第一个日期实际上是 BOM 日期,但如果您将它们格式化为结果中的格式,嗯嗯,它看起来是一样的。如果这是一个问题,事情很容易改变。

如果不希望将第一个月作为 BOM 日期:

  • 将公式更改为:

=IF(EOMONTH(Table1[@[StartDate]:[StartDate]],COLUMNS($A:A)-1)<=Table1[@[EndDate]:[EndDate]],EOMONTH(Table1[@[StartDate]:[StartDate]],COLUMNS($A:A)-1),"")

  • 执行Data Get & Transform时,删除Query GUI编辑器中的StartDate列,因为这不会影响当时的其他列。