【问题标题】:Create separate row for each item when merging multiple workbooks合并多个工作簿时为每个项目创建单独的行
【发布时间】:2017-10-12 20:13:14
【问题描述】:

我有数百个电子表格,我想将它们合并到一个主工作表中。每个电子表格包含多个销售的一般描述信息,然后是一个零件列表,其中包含每个零件特定的信息列,如下所示:

在主表中,我希望每个部分都有一个单独的行,其中包括一般信息以及特定部分信息,如下所示:

我创建了一个循环来提取我想要的所有信息,但是所有信息都在主表中写成一行,如下所示:

谁能告诉我如何为每个项目创建一个单独的行?显示了我拼凑的代码-我认为解决我的问题的方法在于如何格式化标题为“更改此范围以适合您自己的需要”的部分

Sub MergeNT154BatchCards()
Dim MyPath As String, FilesInPath As String
Dim MyFiles() As String
Dim SourceRcount As Long, FNum As Long
Dim mybook As Workbook, BaseWks As Worksheet

Dim dt As String
Dim bookName As String

Dim rnum As Long, CalcMode As Long
Dim a As Range, c As Range
Dim x As Long

Dim sourceRange As Range, destrange As Range

' Change this to the path\folder location of your files.
MyPath = "C:\Users\amiller\OneDrive - CoorsTek\temp"

' Add a slash at the end of the path if needed.
If Right(MyPath, 1) <> "\" Then
    MyPath = MyPath & "\"
End If

' If there are no Excel files in the folder, exit.
FilesInPath = Dir(MyPath & "*.xls*")
If FilesInPath = "" Then
    MsgBox "No files found"
    Exit Sub
End If

' Fill the myFiles array with the list of Excel files
' in the search folder.
FNum = 0
Do While FilesInPath <> ""
    FNum = FNum + 1
    ReDim Preserve MyFiles(1 To FNum)
    MyFiles(FNum) = FilesInPath
    FilesInPath = Dir()
Loop

' Set various application properties.
With Application
    CalcMode = .Calculation
    .Calculation = xlCalculationManual
    .ScreenUpdating = False
    .EnableEvents = False
End With

' Add a new workbook with one sheet.
Set BaseWks = Workbooks.Add(xlWBATWorksheet).Worksheets(1)
    ActiveSheet.Name = "Density"
    bookName = "DensitySummary"
    dt = Format(CStr(Now), "yyyy_mm_dd_hh.mm")
    BaseWks.SaveAs Filename:="C:\Users\amiller\OneDrive - CoorsTek\temp\" & bookName & dt
rnum = 1

Range("A1").Value = "FileName"
Range("B1").Value = "Description"
Range("C1").Value = "WaterTemp(C)"
Range("D1").Value = "WaterDensity(g/cc)"
Range("E1").Value = "PartID"
Range("F1").Value = "DryMass(g)"
Range("G1").Value = "SuspendedMass(g)"
Range("H1").Value = "Density(g/cc)"

' Loop through all files in the myFiles array.
If FNum > 0 Then
    For FNum = LBound(MyFiles) To UBound(MyFiles)
        Set mybook = Nothing
        On Error Resume Next
        Set mybook = Workbooks.Open(MyPath & MyFiles(FNum))
        On Error GoTo 0

        If Not mybook Is Nothing Then
            On Error Resume Next

            ' Change this range to fit your own needs.
            With mybook.Worksheets(1)
                Set R1 = Range("A11, A5, B5")
                Set R2 = Range("A13:D" & Range("A13").End(xlDown).Row)
                Set RF = Union(R1, R2)
                Set sourceRange = RF

            End With

            If Err.Number > 0 Then
                Err.Clear
                Set sourceRange = Nothing
            Else

                ' If source range uses all columns then
                ' skip this file.
                If sourceRange.Columns.Count >= BaseWks.Columns.Count Then
                    Set sourceRange = Nothing
                End If
            End If
            On Error GoTo 0

            If Not sourceRange Is Nothing Then

                SourceRcount = sourceRange.Rows.Count

                If rnum + SourceRcount >= BaseWks.Rows.Count Then
                    MsgBox "There are not enough rows in the target worksheet."
                    BaseWks.Columns.AutoFit
                    mybook.Close savechanges:=False
                    GoTo ExitTheSub
                Else

                    ' Copy the file name in column A.
                    With sourceRange
                        BaseWks.Cells(rnum + 1, "A"). _
                                Resize(.Rows.Count).Value = MyFiles(FNum)
                    End With

                    ' Set the destination range.
                    Set destrange = BaseWks.Range("B" & rnum + 1)

                    x = 0
                    For Each a In sourceRange.Areas
                        For Each c In a.Cells
                            x = x + 1
                            destrange.Offset(0, x - 1).Value = c.Value
                        Next c
                    Next a

                    ' Copy the values from the source range
                    ' to the destination range.
                    With sourceRange
                        Set destrange = destrange. _
                                        Resize(.Rows.Count, .Columns.Count)
                    End With
                    destrange.Value = sourceRange.Value

                    rnum = rnum + SourceRcount
                End If
            End If
            mybook.Close savechanges:=False
        End If

    Next FNum
    BaseWks.Columns.AutoFit
End If

ExitTheSub:
    ' Restore the application properties.
    With Application
        .ScreenUpdating = True
        .EnableEvents = True
        .Calculation = CalcMode
    End With
End Sub

【问题讨论】:

  • 每次处理源工作表中的新行时,开始写入主工作表中的新行。 (没有看到您的代码,这大概是我能建议的最好的了。)要创建 MCVE,请复制您的代码并删除所有与您的问题无关的行。运行您最终得到的内容,以确保它仍然按照您在问题中说明的方式运行,然后发布该代码。
  • 感谢您的评论 - 我已经包含了代码的精简版本。我不确定我的问题可能出在从原始工作表中选择数据的部分(“更改此范围以适合您自己的需要”)或将数据写入新工作表的部分(“复制从源范围到目标范围的值”)。

标签: vba excel


【解决方案1】:

我有点担心,因为您写到主表的标题似乎与数据不一致,而且您似乎只是从每张表的顶部复制Range("A11, A5, B5"),但是您的图片显示了从顶部获取的 5 个字段,但我认为您可以将 For FNum 循环替换为以下内容:

For FNum = LBound(MyFiles) To UBound(MyFiles)
    Set mybook = Nothing
    On Error Resume Next
    Set mybook = Workbooks.Open(MyPath & MyFiles(FNum))
    On Error GoTo 0

    If Not mybook Is Nothing Then
        With mybook.Worksheets(1)
            Set SourceRange = .Range("A13:D" & .Range("A13").End(xlDown).Row)

            SourceRcount = SourceRange.Rows.Count

            If rnum + SourceRcount >= BaseWks.Rows.Count Then
                MsgBox "There are not enough rows in the target worksheet."
                BaseWks.Columns.AutoFit
                mybook.Close savechanges:=False
                GoTo ExitTheSub
            Else

                ' Copy the file name in column A.
                BaseWks.Cells(rnum + 1, "A").Resize(SourceRcount).Value = MyFiles(FNum)
                ' Copy information such as date/time started, start/final temp, and Batch ID
                BaseWks.Cells(rnum + 1, "B").Resize(SourceRcount).Value = .Range("A4").Value
                BaseWks.Cells(rnum + 1, "C").Resize(SourceRcount).Value = .Range("B4").Value
                BaseWks.Cells(rnum + 1, "D").Resize(SourceRcount).Value = .Range("A5").Value
                BaseWks.Cells(rnum + 1, "E").Resize(SourceRcount).Value = .Range("A5").Value
                BaseWks.Cells(rnum + 1, "F").Resize(SourceRcount).Value = .Range("A11").Value
                'Copy main data
                BaseWks.Cells(rnum + 1, "G").Resize(SourceRcount, SourceRange.Columns.Count).Value = SourceRange.Value

                rnum = rnum + SourceRcount
            End If
        End With
    End If
    mybook.Close savechanges:=False
Next FNum

【讨论】:

  • 这正是我所需要的,谢谢!感谢您耐心地解决我草率的问题。你是对的;我发布的代码(仅复制 A11、A5 和 B5)适用于旧格式。
  • 仅供参考-我不得不将代码的顺序从最后的 End If, End If, End With 交换到 End If, End With, End If 但其他一切都运行良好!
  • 感谢您接错订单。我已经相应地编辑了答案。 (Mat's Mug 现在可能会出现并提醒我,如果我安装了RubberDuck,就不会发生这种情况,他是对的。)
【解决方案2】:

问题的根源在于您试图在单个子例程中做太多事情。当您的子程序超过 25-40 行时,您应该考虑将功能提取到更小的子程序中。通过这种方式,您将能够一次测试更小部分的代码。

通过实施这一策略,我设法将 OPs 原始子程序从 152 行代码减少到 5 个易于调试的 80 行代码子程序。

  1. MergeNT154BatchCards - 主子例程
  2. AddBatchCard - 打开工作簿并将新的数据行添加到范围
  3. getDensityTemplate - 根据模板创建新工作簿
  4. getFileList - 从目录中获取文件列表
  5. ToggleEvents - 关闭和打开事件并返回当前的计算模式

我还没有测试代码的某些部分,正如@YowE3K 所说,标题没有对齐。我认为使用这些较小的代码块修改代码以适应 OP 要求将相当容易。


Public Sub MergeNT154BatchCards()
    Dim vFiles As Variant, FileFullName As Variant
    Dim NextRow As Range, wb As Workbook
    Dim CalculationMode As XlCalculation
    CalculationMode = ToggleEvents(False, xlCalculationManual)

    vFiles = getFileList("C:\Users\best buy\Downloads\stackoverfow", "*.xls*")
    If UBound(vFiles) = -1 Then
        MsgBox "No files found", vbInformation, ""
        Exit Sub
    End If

    Set wb = getDensityTemplate

    For Each FileFullName In vFiles
        With wb.Worksheets(1)
            'Add Header
            .Range("A1:H1").Value = Array("FileName", "Description", "WaterTemp(C)", "WaterDensity(g/cc)", "PartID", "DryMass(g)", "SuspendedMass(g)", "Density(g/cc)")
            'Target the next empty row
            Set NextRow = .Range("A" & .Rows.Count).End(xlUp).Offset(1)
            AddBatchCard CStr(FileFullName), NextRow
        End With
    Next

    ToggleEvents True, CalculationMode
End Sub

Private Sub AddBatchCard(FileFullName As String, NextRow As Range)
    Dim cell As Range
    Dim x As Long, y As Long
    With Workbooks.Open(FileFullName)
        With .Worksheets(1)
            For Each cell In .Range("A13", .Range("A" & .Rows.Count).End(xlUp)).Value
                'NextRow
                NextRow.Cells(1, 1).Value = .Range("A4").Value
                NextRow.Cells(1, 2).Value = .Range("B4").Value
                NextRow.Cells(1, 3).Value = .Range("A5").Value
                NextRow.Cells(1, 4).Value = .Range("B5").Value
                NextRow.Cells(1, 4).Resize(1, 4).Value = cell.Resize(1, 4).Value
                Set NextRow = NextRow.Offset(1)
            Next
        End With
        .Close SaveChanges:=False
    End With
End Sub

Private Function getDensityTemplate(FilePath As String) As Workbook
    Dim SheetsInNewWorkbook As Integer
    Dim wb As Workbook
    SheetsInNewWorkbook = Application.SheetsInNewWorkbook
    Application.SheetsInNewWorkbook = 1

    Set wb = Workbooks.Add(xlWBATWorksheet)
    wb.Worksheets(1).Name = "Density"
    wb.SaveAs FileName:=FilePath & "DensitySummary" & Format(Now, "yyyy_mm_dd_hh.mm")
    Set getDensityTemplate = wb
End Function

Private Function getFileList(FilePath As String, PatternSearch As String) As Variant
    Dim FileName As String
    If Right(FilePath, 1) <> "\" Then FilePath = FilePath & "\"

    With CreateObject("System.Collections.ArrayList")
        FileName = Dir(FilePath & PatternSearch)
        Do While FileName <> ""
            .Add FilePath & FileName
            FileName = Dir()
        Loop
        getFileList = .ToArray
    End With
End Function

Private Function ToggleEvents(EnabelEvents As Boolean, CalculationMode As XlCalculation) As XlCalculation
    With Application
        ToggleEvents = .Calculation
        .Calculation = CalculationMode
        .ScreenUpdating = EnabelEvents
        .EnableEvents = EnabelEvents
    End With
End Function

【讨论】:

  • 感谢您这样做。我对 VBA 的经验很少,所以我很感激我能得到的任何建议。这个解决方案比我的蛮力方法优雅得多......而且更容易理解。我将不得不做一些小的调整,但你的代码将成为未来项目的一个很好的模板!
猜你喜欢
  • 1970-01-01
  • 2022-12-24
  • 1970-01-01
  • 2014-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多