【问题标题】:Split data into multiple workbooks based on cell value in Excel using vba使用vba根据Excel中的单元格值将数据拆分为多个工作簿
【发布时间】:2017-10-10 03:40:54
【问题描述】:

每个月我都会收到我们的销售报告,其中包含我们销售的商品数量以及产品详细信息,我使用 vba 创建了一个模板,用户可以在其中指定产品,它可以为他们创建一个 Excel 报告。

但是,如果我有多个 Excel 报表而不是只有一个报表,我想扩展/修改。我想用 excel 来区分我输入或列出的许多产品代码。

现在,我在模板中添加了一个名为 list 的选项卡,我可以在其中列出 vba 应该从中读取的产品代码编号(A 列中的 4 位数字),但我需要帮助修改代码,而不是询问用户,它改为读取列表。其次,由于主文件包含所有产品,我可能只需要其中的 20 或 30 个,因此我需要 vba 代码尽可能灵活。

按照我的设置方式,我基本上是将主文件中的新信息更新/复制到月度模板中,并将月度模板重新保存为 9.1.2017 文件中的产品代码产品。

Sub monthly()


Dim x1 As Workbook, y1 As Workbook
Dim ws1, ws2 As Worksheet
Dim LR3, LR5 As Long
Dim ws3 As Worksheet
Dim Rng3, Rng4 As Range
Dim x3 As Long

Set x1 = Workbooks("Master.xlsx")
Set y1 = Workbooks("Monthly Template.xlsm")

Set ws1 = x1.Sheets("Products")
Set ws2 = y1.Sheets("Products")
Set ws3 = y1.Sheets("List")

ws2.Range("A3:AA30000").ClearContents
ws1.Cells.Copy ws2.Cells

x1.Close True

LR5 = ws3.Cells(Rows.Count, "A").End(xlUp).Row

With y1.Sheets("List")
Range("A1:A32").Sort key1:=Range("A1"), Order1:=xlAscending
End With





LR3 = ws2.Cells(Rows.Count, "A").End(xlUp).Row


Set Rng3 = ws2.Range("AC3:AC" & LR3)

Set Rng4 = ws3.Range("A1:A" & LR5)

For n = 3 To LR3
ws2.Cells(n, 29).FormulaR1C1 = "=LEFT(RC[-21], 4)"
Next n



With y1.Sheets("List")
    j = .Cells(.Rows.Count, 1).End(xlUp).Row
End With
With ws2
    l = .Cells(.Rows.Count, 1).End(xlUp).Row
End With
For i = 1 To j
    For k = 3 To l
        If Sheets("List").Cells(i, 1).Value = Sheets("Products").Cells(k, 29).Value Then
            With Sheets("Output")
                m = .Cells(.Rows.Count, 1).End(xlUp).Row
            End With
            Sheets("Output").Rows(m + 1).Value = Sheets("Products").Rows(k).Value
        End If
    Next k
Next i

Sheets("Output").Columns("AC").ClearContents


   Dim cell As Range
    Dim dict As Object, vKey As Variant
    Dim Key As String
    Dim SheetsInNewWorkbook As Long
    Dim DateOf As Date


    DateOf = DateSerial(Year(Date), Month(Date), 1)

    With Application
        .ScreenUpdating = False
        SheetsInNewWorkbook = .SheetsInNewWorkbook
        .SheetsInNewWorkbook = 1
    End With

    Set dict = CreateObject("Scripting.Dictionary")
    With ThisWorkbook.Worksheets("List")
        For Each cell In .Range("A1", .Range("A" & .Rows.Count).End(xlUp))
            Key = Left(cell.Value, 4)
            'Store an ArrayList in the Scripting.Dictionary that can be retrieved using the Product Key
            If Not dict.exists(Key) Then dict.Add Key, CreateObject("System.Collections.ArrayList")
        Next
    End With

    With Workbooks("Monthly Template.xlsm").Worksheets("Output")
        For Each cell In .Range("H2", .Range("A" & .Rows.Count).End(xlUp))
            Key = Left(cell.Value, 4)
            'Add the Products to the ArrayList in the Scripting.Dictionary that is associated with the Product Key
            If dict.exists(Key) Then dict(Key).Add cell.Value
        Next
    End With

    For Each vKey In dict
        If dict(vKey).Count > 0 Then
            With Workbooks.Add
                With .Worksheets(1)
                    .Name = "Products"
                   ' .Range("A1").Value = "Products"

                    Workbooks("Monthly Template.xlsm").Worksheets("Output").Cells.Copy Worksheets(1).Cells

                      For Z = 1 To LR5
                      For x3 = Rng3.Rows.Count To 1 Step -1
                        If InStr(1, Rng3.Cells(x3, 1).Text, Workbooks("Monthly Template.xlsm").Worksheets("List").Cells(Z, 1).Text) = 0 Then
                            Rng3.Cells(x3, 1).EntireRow.Delete
                        End If
                        Next x3
                        Next Z


                    '.Range("A2").Resize(dict(vKey).Count).Value = Application.Transpose(dict(vKey).ToArray)
                End With
                .SaveAs Filename:=getMonthlyFileName(DateOf, CStr(vKey)), FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
                .Close SaveChanges:=False
            End With
        End If
    Next

    With Application
        .ScreenUpdating = True
        .SheetsInNewWorkbook = SheetsInNewWorkbook
    End With

End Sub

Function getMonthlyFileName(DateOf As Date, Product As String) As String
    Dim path As String

    path = ThisWorkbook.path & "\Product Reports\"

    If Len(Dir(path, vbDirectory)) = 0 Then MkDir path

    path = path & Format(DateOf, "yyyy") & "\"

    If Len(Dir(path, vbDirectory)) = 0 Then MkDir path

    path = path & Format(DateOf, "mmm") & "\"

    If Len(Dir(path, vbDirectory)) = 0 Then MkDir path

    getMonthlyFileName = path & "Product - " & Product & Format(DateOf, " mmm.dd.yyyy") & ".xlsx"
End Function

【问题讨论】:

    标签: vba excel


    【解决方案1】:

    我认为没有理由保存 Monthly Template.xlsm 的副本。 OP 的代码只是在工作表上创建一个列表并将其保存到文件中。我可能缺少一些通常会从主文件中保存的格式。

    getMonthlyFileName(DateOf, Product) - 创建文件路径(根路径\日期年份\日期月份\产品 - 产品 mmm.dd.yyyy.xlsx。这样,产品文件可以存储在易于查找的结构中.

    Sub CreateMonthlyReports()
        Dim cell As Range
        Dim dict As Object, vKey As Variant
        Dim Key As String
        Dim SheetsInNewWorkbook As Long
        Dim DateOf As Date
    
        DateOf = DateSerial(Year(Date), Month(Date), 1)
    
        With Application
            .ScreenUpdating = False
            SheetsInNewWorkbook = .SheetsInNewWorkbook
            .SheetsInNewWorkbook = 1
        End With
    
        Set dict = CreateObject("Scripting.Dictionary")
        With ThisWorkbook.Worksheets("List")
            For Each cell In .Range("A1", .Range("A" & .Rows.Count).End(xlUp))
                Key = Left(cell.Value, 4)
                'Store an ArrayList in the Scripting.Dictionary that can be retrieved using the Product Key
                If Not dict.exists(Key) Then dict.Add Key, CreateObject("System.Collections.ArrayList")
            Next
        End With
    
        With Workbooks("Master.xlsx").Worksheets("Products")
            For Each cell In .Range("H2", .Range("H" & .Rows.Count).End(xlUp))
                Key = Left(cell.Value, 4)
                'Add the Products to the ArrayList in the Scripting.Dictionary that is associated with the Product Key
                If dict.exists(Key) Then dict(Key).Add cell.Value
            Next
        End With
    
        For Each vKey In dict
            If dict(vKey).Count > 0 Then
                With Workbooks.Add
                    With .Worksheets(1)
                        .Name = "Products"
                        .Range("A1").Value = "Products"
                        .Range("A2").Resize(dict(vKey).Count).Value = Application.Transpose(dict(vKey).ToArray)
                    End With
                    .SaveAs FileName:=getMonthlyFileName(DateOf, CStr(vKey)), FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
                    .Close SaveChanges:=False
                End With
            End If
        Next
    
        With Application
            .ScreenUpdating = True
            .SheetsInNewWorkbook = SheetsInNewWorkbook
        End With
    
    End Sub
    
    Function getMonthlyFileName(DateOf As Date, Product As String) As String
        Dim path As String
    
        path = ThisWorkbook.path & "\Product Reports\"
    
        If Len(Dir(path, vbDirectory)) = 0 Then MkDir path
    
        path = path & Format(DateOf, "yyyy") & "\"
    
        If Len(Dir(path, vbDirectory)) = 0 Then MkDir path
    
        path = path & Format(DateOf, "mmm") & "\"
    
        If Len(Dir(path, vbDirectory)) = 0 Then MkDir path
    
        getMonthlyFileName = path & "Product - " & Product & Format(DateOf, " mmm.dd.yyyy") & ".xlsx"
    End Function
    

    【讨论】:

    • 嗨,抱歉,我花了一段时间才重新开始这项任务,但我有一些问题。我发布了一些图片,因此它可以帮助您可视化我的结果,所以我假设我需要使用 Cyril 和您的修改我的原始代码。问题 1) List 中的代码已经是父代码(4 位数字)所以我不知道为什么我们需要在 Products 和 List 中这样做。所以列表中有我需要生成报告的产品
    • 2) 您的代码确实给了我单独的报告,但是,当我打开 vba 生成的 32 份报告时,它只给了我一个产品,所以有些东西是关闭的。我将 Master 更新为月度模板,因为我的代码已经将新数据导入月度模板。
    • 最后一个是我想要的结果。我可以重新格式化,但现在的问题是它只填充一个产品,其次,我们真的不需要找到父代码 (left(cell,4),因为会给出列表。
    • 我将我的原始代码与 Cyril 的代码合并,让我尝试合并你的代码。我仍然不太了解 2 with 您尝试添加 /dict(Key).Add cell.Value/ 的语句
    • 好的,现在我无法传输数据...我根据您的输入更新了代码,您能帮忙吗?它传输了所有产品数据,但不传输特定产品。
    【解决方案2】:

    为此尝试两个循环,确保按主列表中的产品排序,以加快速度。

    Dim i as Long, j as Long, k as Long, l as Long, m as Long
    With Sheets("List")
        j = .Cells( .Rows.Count, 1).End(xlUp).Row
    End With
    With Sheets("Products")
        l = .Cells( .Rows.Count, 1).End(xlUp).Row
    End With
    For i = 2 to j
        For k = 2 to l
            If Sheets("List").Cells(i,1).Value = Sheets("Products").Cells(k,1).Value Then
                With Sheets("Output")
                    m = .Cells( .Rows.Count, 1).End(xlUp).Row
                End With
                Sheets("Output").Rows(m+1).Value = Sheets("Products").Rows(k).Value
            End If
        Next k
    Next i
    

    编辑

    将尝试零碎一些东西至少导致分成不同的工作表,而不是只有一个输出工作表(这不会被测试,只是免费编码):

    Dim i as Long, j as Long, k as Long, l as Long, m as Long, n as String
    With Sheets("List")
        j = .Cells( .Rows.Count, 1).End(xlUp).Row
    End With
    With Sheets("Products")
        l = .Cells( .Rows.Count, 1).End(xlUp).Row
    End With
    For i = 2 to j
        n = Sheets("List").Cells(i,1).Value
        Sheets.Add(After:=Sheets(Sheets.Count)).Name = n
        Sheets(n).Cells(1,1).Value = n
        Sheets(n).Rows(2).Value = Sheets("Products").Rows(1).Value
        For k = 2 to l
            With Sheets(n)
                If .Cells(1,1).Value = Sheets("Products").Cells(k,1).Value Then
                m = .Cells( .Rows.Count, 1).End(xlUp).Row
                .Rows(m+1).Value = Sheets("Products").Rows(k).Value
            End If
        Next k
    Next i
    

    【讨论】:

    • 我没有检查你的输出在哪里,所以我只使用了 Sheets("Output")。确保您在 Columns(1)/Columns("A") 中有一个标题,因为这为“m”提供了一个起点。假设 Sheets("List") 是用户输入他们的产品列表的地方,而 Sheets("Products") 是一个主列表。
    • @sc1324 所以在这种情况下,Sheets("Output") 的每个实例都会变成 Sheets("Monthly Template"),是吗?
    • @sc1324 现在,假设每个工作表都在同一个工作簿中,此代码将是独立的。您可能希望在包含人们正在填写的列表的工作簿中使用它(使用 Sheets("List"),很可能与运行此代码的“GO”按钮相关联。看起来您正在使用不同的工作簿,您将需要对此代码中的对象(对象为 Workbook.Sheet.Range)进行调整。
    • 我改变了我的问题,所以希望这有助于澄清我需要实现@Cyril
    • @sc1324 公平地说,您最初的问题已得到解答;应用这个会更合适,然后问另一个关于分离的问题,特别是因为这需要更多的工作。将每个唯一值获取到自己的工作表中,然后为每个工作表运行一个循环,这将是最直接的方法;然后,您就可以将工作表移动到新工作簿。
    【解决方案3】:

    我不知道为什么有些做VBA的人认为在一千行代码之前用奇怪的名字声明所有变量是个好主意............

    无论如何..回到问题,我相信你想要实现的是:

    1) 指定一个列表,同时代码遍历列表并根据列出的项目过滤数据。 2) 创建一个工作簿,在其中复制过滤的数据。 3) 使用特定名称将工作簿保存到您指定的位置。

    所以很自然,您的程序访问点应该是遍历指定列表的访问点,这应该是您的主函数。

    然后在 main 函数中,您将拥有一个处理任何产品 ID 的 Sub,然后过滤您的产品 ID,然后将数据复制到新创建的工作簿中。

    最后一步是命名新工作簿并保存并关闭它。

    这里有一些代码框架,希望能帮助您创建月度报告。您必须自己编写如何将数据从主工作簿复制到目标工作簿(应该很简单,只需过滤源列表并将结果复制到目标工作簿,不需要字典或数组列表) .

    Sub main()
        Dim rngIdx As Range
        Set rngIdx = ThisWorkbook.Sheets("where your list is").Range("A1")
    
        With Application
            .DisplayAlerts = False
            .ScreenUpdating = False
        End With
    
        While (rngIdx.Value <> "")
            Call create_report(rngIdx.Value)
            Set rngIdx = rngIdx.Offset(1, 0)
        Wend
    
        With Application
            .DisplayAlerts = True
            .ScreenUpdating = True
        End With
    End Sub
    
    Sub create_report(ByVal product_ID As String)
        Dim dest_wbk As Workbook
        Set dest_wbk = Workbooks.Add
    
        Call do_whatever(ThisWorkbook, dest_wbk, product_ID)
    
        dest_wbk.SaveAs getMonthlyFileName(some_date, product_ID)
        dest_wbk.Close
    
    End Sub
    
    Sub do_whatever(source_wbk As Workbook, dest_wbk As Workbook, ByVal product_ID As String)
        ' this is the code where you copy from your master data to the destination workbook
        ' modify sheet names, formatting.......etc.
    End Sub
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-28
      • 1970-01-01
      • 2015-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多