【问题标题】:Looping through worksheets in a workbook and consolidating each workbook in to a worksheet in the master workbook循环浏览工作簿中的工作表并将每个工作簿合并到主工作簿中的工作表中
【发布时间】:2020-03-08 16:43:51
【问题描述】:

我已经搜索并搜索了我的代码问题的答案,但我找不到任何答案。如果有人可以查看我的代码,我将不胜感激。目前,我有几个大型工作簿,用于每个国家/地区的数据。每个工作簿有超过 5 个工作表。我想将工作簿合并到一个主文件中。首先,我要复制并粘贴主工作簿中一张工作表下的所有工作表,并按国家/地区命名。现在,我的代码一次只能合并一个国家,这使得它非常慢。循环工作表似乎也失败了。它只创建一个国家工作表。如果我输入多个国家/地区名称,则只有最后一个国家/地区工作簿得到合并。缺少一些东西,但我似乎无法弄清楚。太感谢了!!!!以下是我的代码:

Sub consolidate()

   Application.EnableCancelKey = xlDisabled

   Dim folderPath As String
   Dim Filename As String
   Dim wb As Workbook
   Dim Masterwb  As Workbook
   Dim sh As Worksheet
   Dim NewSht As Worksheet
   Dim FindRng As Range
   Dim PasteRow As Long

   Dim countryname As String
   Dim LastRow, Rowlast, Rowlast2 As Long
   Const fr As Long = 2
   Dim i As Long
   Dim cell As Range
   Dim wx As Worksheet
   Set wx = ThisWorkbook.Sheets("Countryname")
   Rowlast = wx.Range("B" & Rows.Count).End(xlDown).row 'selects list of country workbook I want to consolidate. e.g I could have Germany, usa, china
   Rowlast2 = wx.Range("C" & Rows.Count).End(xlDown).row 'selects list of tabs for each country workbook I want to consolidate, e.g I want for every country listed above, that sheet names 1, 2, 3, 4 be consolidated and put in new worksheets in the masterfile

   With wx
      For LastRow = fr To Rowlast
         If .Cells(LastRow, "B").Value <> "" Then
            countryname = .Cells(LastRow, "B").Value
            ' set master workbook
            Set Masterwb = Workbooks("ebele_test.xlsm")
            folderPath = Application.InputBox(Prompt:= _
                  "Please enter only folder path in this format as C:\Users\...  Exclude the file name", _
            Title:="InputBox Method", Type:=2) 'Type:=2 = text

            If folderPath = "False" Or IsError(folderPath) Then 'If Cancel is clicked on Input Box exit sub

               MsgBox "Incorrect Input, Please paste correct folder path"
               Exit Sub
               'On Error GoTo 0

            End If
            If Right(folderPath, 1) <> "\" Then folderPath = folderPath & "\"
            Application.ScreenUpdating = False
            Dim str As String
            str = "Screener_User_Template-"

            Filename = Dir(folderPath & str & countryname & "*.xlsx")
            Do While Filename <> ""
               Set wb = Workbooks.Open(folderPath & Filename)

               If Len(wb.Name) > 253 Then
                  MsgBox "Sheet's name can be up to 253 characters long, shorten the Excel file name"
                  wb.Close False
                  GoTo Exit_Loop
               Else
                  ' add a new sheet with the file's name (remove the extension)
                  With Masterwb
                     Dim isLastSheet As Boolean
                     Dim ci, rows1 As Integer
                     Dim row As Long
                     rows1 = ThisWorkbook.Worksheets.Count
                     For ci = rows1 To 1 Step (-1)
                        If (isLastSheet) = False Then
                           Set NewSht = Masterwb.Worksheets.Add(After:=Worksheets(ci)) 'Place sheet at the end.
                           NewSht.Cells(1, 1) = "Identifier"
                           NewSht.Cells(1, 2) = "Company Name"
                           NewSht.Cells(1, 3) = "Country of Incorporation"
                           NewSht.Name = countryname
                        End If
                     Next ci
                  End With

               End If

               ' loop through all sheets in opened wb

               For Each sh In wb.Worksheets
                  For i = 2 To Rowlast2
                     If sh.Name = wx.Cells(i, "C").Value And NewSht.Name = countryname Then
                        ' get the first empty row in the new sheet

                        Set FindRng = NewSht.Cells.Find(What:="*", Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False)

                        If Not FindRng Is Nothing Then ' If find is successful
                           PasteRow = FindRng.row + 1
                        Else ' find was unsuccessfull > new empty sheet, should paste at the second row
                           PasteRow = 2
                        End If

                        Dim rng As Range
                        Set rng = sh.Range(sh.Cells(3, "A"), sh.Cells(150000, "M"))
                        rng.Copy

                        NewSht.Range("A" & PasteRow).PasteSpecial xlPasteValues

                     End If
                     Application.CutCopyMode = False 'Clears the clipboard
                  Next i
               Next sh
               wb.Close False
Exit_Loop:
               Set wb = Nothing
               Filename = Dir
            Loop
         End If
      Next LastRow
   End With
   '0:  Exit Sub
   Application.ScreenUpdating = True
End Sub

【问题讨论】:

  • 这是需要通读的大量代码。你能解释一下你的代码要完成的一般步骤吗
  • wx.Range("B" &amp; Rows.Count).End(xlDown).row 这看起来不对,我想你想要wx.Range("B" &amp; wx.Rows.Count).End(xlup).row,与“C”列相同
  • @O.PAL:“Application.Rows.Count”、“Rows.Count”和“wx.Rows.Count”都返回相同的数字。总结:“Rows.Count”很好。不过,您对“xlUp”的看法是正确的。
  • 这里没有足够的信息供任何人提供逻辑错误的解决方案。我们可以指出其他语法错误或编码中可能出现的混淆,例如您的行 Dim LastRow, Rowlast, Rowlast2 As Long。你实际上只是声明Rowlast2。前两个变量未指定,默认输入Variant

标签: excel vba


【解决方案1】:

一团糟

不是解决方案,只是一项正在进行的工作,由于缺乏信息和知识,我无法继续。它可以帮助你完成你开始的事情。在你投入了这么多时间之后退出将是一种耻辱。如果您从代码中的问题中提供一些答案,其他人可能会帮助您完成它。这些问题绝不具有讽刺意味,它们是我无法肯定回答的严肃问题。

代码应该是安全的,但只是不要保存任何东西以免丢失数据。

我建议你以某种方式将这样的代码分成几个,然后问几个问题以便将来得到答案。

Option Explicit

Sub Consolidate()

  Application.EnableCancelKey = xlDisabled

  ' ThisWorkbook
  Const cStrCountry As String = "CountryName"
  Const cLngRow1 As Long = 2
  ' Tip:  To use columns either as string or as integer declare them as Variant.
  Const cVntColCountries As Variant = "B"
  Const cVntColTabs As Variant = "C"

  Const cStrTemplate = "Screener_User_Template-"
  Const cStrMaster As String = "ebele_test.xlsm"
  Const cStrExt = ".xlsx"

  ' New Worksheet in Master Workbook
  Const cStrNewHeader1 = "Identifier"
  Const cStrNewHeader2 = "Company Name"
  Const cStrNewHeader3 = "Country of Incorporation"

  ' Each Worksheet in Each Workbook
  Const cLngFirstRow As Long = 3
  Const cLngLastRow As Long = 150000
  ' Tip:  To use columns either as string or as integer declare them as Variant.
  Const cVntFirstCol As Variant = "A"
  Const cVntLastCol As Variant = "M"

  ' MsgBox
  Dim strMsg1 As String
    strMsg1 = "Please enter only folder path in this format as " _
            & "C:\Users\... Exclude the file name"
  Dim strMsg2 As String
    strMsg2 = "Incorrect Input. Please paste correct folder path."
  Dim strMsg3 As String
    strMsg3 = "Sheet's name can only be up to 253 characters long. " _
            & "Shorten the Excel file name."

  ' Workbooks
'  ThisWorkbook
  Dim ojbWbEach As Workbook     ' Workbook Looper
  Dim objWbMaster As Workbook   ' Master Workbook

  ' Worksheets
'  ThisWorkbook.Worksheets (cStrCountry)
  Dim objWsEach As Worksheet    ' Worksheet Looper
  Dim objWsNew As Worksheet     ' New Worksheet

  ' Arrays Pasted From Ranges
  Dim vntCountries As Variant   ' List of Countries
  Dim vntTabs As Variant        ' List of Tabs

  ' Ranges
  Dim objRngEmpty As Range      ' New Sheet Paste Cell

  ' Rows
  Dim lngPasteRow As Long       ' New Sheet Paste Row
  Dim lngCountries As Long      ' Countries Counter
  Dim lngTabs As Long           ' Tabs Counter

  ' Strings
  Dim strPath As String
  Dim strFile As String
  Dim strCountry As String

  With ThisWorkbook.Worksheets(cStrCountry)

    ' Paste list of countries from column cVntColCountries into array
    vntCountries = .Range(.Cells(cLngRow1, cVntColCountries), _
        .Cells(Rows.Count, cVntColCountries).End(xlUp)).Value2

    ' Paste list of tabs from column cVntColTabs into array
    vntTabs = .Range(.Cells(cLngRow1, cVntColTabs), _
        .Cells(Rows.Count, cVntColTabs).End(xlUp)).Value2

  End With
  ' The data is in arrays instead of ranges.

  ' 1. According to the following line the workbook objWbMaster is already open.
  '    Is that true?
  Set objWbMaster = Workbooks(cStrMaster)

  For lngCountries = LBound(vntCountries) To UBound(vntCountries)

    If vntCountries(lngCountries, 1) <> "" Then

        strCountry = vntCountries(lngCountries, 1)

        ' Determine the path to search for files in.
        strPath = Application.InputBox(Prompt:=strMsg1, _
          Title:="InputBox Method", Type:=2) ' Type:=2 = text

        ' When Cancel is clicked in Input Box ... Exit Sub
        If strPath = "False" Or IsError(strPath) Then
          MsgBox strMsg2
          Exit Sub
        End If
        If Right(strPath, 1) <> "\" Then strPath = strPath & "\"

        Application.ScreenUpdating = False

        strFile = Dir(strPath & cStrTemplate & strCountry & "*" & cStrExt)
        ' VBA Help: Dir returns the first file name that matches pathname. To
        '           get any additional file names that match pathname, call Dir
        '           again with no arguments. When no more file names match, Dir
        '           returns a zero-length string ("").
        ' i.e. The approach is correct!
        Do While strFile <> ""

          Set ojbWbEach = Workbooks.Open(strPath & strFile)

          ' 2. When would this ever happen?
          If Len(ojbWbEach.Name) <= 253 Then
            ' Add a new sheet with the file's name (remove the extension)
            With objWbMaster
              ' 3. Isn't the blnLastSheet always False. What should it be doing?
              Dim blnLastSheet As Boolean
              Dim intSheetsCounter As Integer
              Dim intSheets As Integer
              intSheets = .Worksheets.Count
              ' 4. Why parentheses in ... Step (-1)?
              For intSheetsCounter = intSheets To 1 Step -1
                ' 5. Why parentheses in (blnLastSheet)?
                If (blnLastSheet) = False Then
                  ' Place sheet at the end.
                  Set objWsNew = .Worksheets _
                      .Add(After:=.Worksheets(intSheetsCounter))
                  With objWsNew
                    .Cells(1, 1) = cStrNewHeader1
                    .Cells(1, 2) = cStrNewHeader2
                    .Cells(1, 3) = cStrNewHeader3
                    .Name = strCountry
                  End With
                End If
              Next
            End With
           Else
            MsgBox strMsg3
            ojbWbEach.Close False
            GoTo Exit_Loop
          End If

          ' Loop through all worksheets in ojbWbEach.
          For Each objWsEach In ojbWbEach.Worksheets
            With objWsEach

              For lngTabs = LBound(vntTabs) To UBound(vntTabs)
                If .Name = vntTabs(lngTabs) _
                    And objWsNew.Name = strCountry Then

                  ' Get the first empty row in the new sheet
                  Set objRngEmpty = objWsNew.Cells.Find(What:="*", _
                      Lookat:=xlPart, LookIn:=xlFormulas, _
                      SearchOrder:=xlByRows, SearchDirection:=xlPrevious)

                  ' 6. I don't think that this is necessary because you added
                  '    the headers to the New sheet so it will find the first
                  '    row. Or am I missing something?
                  If Not objRngEmpty Is Nothing Then
                    ' If find is successful.
                    lngPasteRow = objRngEmpty.row + 1
                   Else
                    ' Find was unsuccessfull > new empty sheet.
                    ' Should paste at the second row.
                    lngPasteRow = cLngRow1
                  End If

                  ' if I'm right, delete all starting from "Set objRngEmpty ..."
                  ' and delete "Dim objRngEmpty as Range" and use the following
                  ' line:
'                  lngPasteRow = objWsNew.Cells.Find(What:="*", Lookat:=xlPart, _
                      LookIn:=xlFormulas, SearchOrder:=xlByRows, _
                      SearchDirection:=xlPrevious).row + 1

                  ' Pasting a range into a same sized range is much faster than
                  ' looping or copy/pasting.
                  objWsNew.Range(.Cells(lngPasteRow, cVntFirstCol), _
                      .Cells(cLngLastRow + lngPasteRow - cLngFirstRow, _
                      cVntLastCol)) = _
                  .Range(.Cells(cLngFirstRow, cVntFirstCol), _
                      .Cells(cLngLastRow, cVntLastCol)).Value2

                End If
              Next

              .Close False

            End With
          Next

Exit_Loop:
          Set ojbWbEach = Nothing
          strFile = Dir
        Loop
      End If
    Next lngCountries

  Set objWsEach = Nothing
  Set objWsNew = Nothing
  Set objWbEach = Nothing
  Set objWbMaster = Nothing

  Application.ScreenUpdating = True

End Sub

【讨论】:

  • 你是绝对正确的。它一团糟,我应该在发布之前清理一些死代码,但我担心我会在整天工作后破坏某些东西,它最终在凌晨 2 点(德国时间)合并,尽管它没有完全按照我的预期工作。我本来打算带着我的“半面包”跑。
  • 非常感谢您的清理工作。回答你的一些问题。 Q1:是的,它是开放的。它是主工作簿,其中打开了一个工作表,其中包含国家列表和选项卡名称列表。 Q2:令人惊讶的是,有时位置文件夹名称太长了,太疯狂了。毕竟我在德国。我们对事物使用长名称。对不起! Q 3,4 和 5:我不记得我为什么这样做了。我想我写了一个代码并修改了它。 Q6:那是一个剩余的代码。起初它粘贴了每张纸上的所有标题,我不希望这样。太感谢了!我会告诉你它是如何工作的。
【解决方案2】:

再次感谢您的清理。我对您的代码进行了一些修改并纠正了一些错误,但由于某种原因,它只能合并 7 个国家/地区,之后 excel 崩溃。请看下面我运行的代码:你认为你能找到问题吗?

选项显式

Sub Consolidate()

  Application.EnableCancelKey = xlDisabled

  ' ThisWorkbook
  Const cStrCountry As String = "CountryName"
  Const cLngRow1 As Long = 2
  ' Tip:  To use columns either as string or as integer declare them as Variant.
  Const cVntColCountries As Variant = "B"
  Const cVntColTabs As Variant = "C"

  Const cStrTemplate = "Screener_User_Template-"
  Const cStrMaster As String = "ebele_test.xlsm"
  Const cStrExt = ".xlsx"

  ' New Worksheet in Master Workbook
  Const cStrNewHeader1 = "Identifier"
  Const cStrNewHeader2 = "Company Name"
  Const cStrNewHeader3 = "Country of Incorporation"

  ' Each Worksheet in Each Workbook
  Const cLngFirstRow As Long = 3
  Const cLngLastRow As Long = 150000
  ' Tip:  To use columns either as string or as integer declare them as Variant.
  Const cVntFirstCol As Variant = "A"
  Const cVntLastCol As Variant = "M"

  ' MsgBox
  Dim strMsg1 As String
    strMsg1 = "Please enter only folder path in this format as " _
            & "C:\Users\... Exclude the file name"
  Dim strMsg2 As String
    strMsg2 = "Incorrect Input. Please paste correct folder path."
  Dim strMsg3 As String
    strMsg3 = "Sheet's name can only be up to 253 characters long. " _
            & "Shorten the Excel file name."

  ' Workbooks
'  ThisWorkbook
  Dim ojbWbEach As Workbook     ' Workbook Looper
  Dim objWbMaster As Workbook   ' Master Workbook

  ' Worksheets
'  ThisWorkbook.Worksheets (cStrCountry)
  Dim objWsEach As Worksheet    ' Worksheet Looper
  Dim objWsNew As Worksheet     ' New Worksheet

  ' Arrays Pasted From Ranges
  Dim vntCountries As Variant   ' List of Countries
  Dim vntTabs As Variant        ' List of Tabs

  ' Ranges
  Dim objRngEmpty As Range      ' New Sheet Paste Cell

  ' Rows
  Dim lngPasteRow As Long       ' New Sheet Paste Row
  Dim lngCountries As Long      ' Countries Counter
  Dim lngTabs As Long           ' Tabs Counter

  ' Strings
  Dim strPath As String
  Dim strFile As String
  Dim strCountry As String

  With ThisWorkbook.Worksheets(cStrCountry)

    ' Paste list of countries from column cVntColCountries into array
    vntCountries = .Range(.Cells(cLngRow1, cVntColCountries), _
        .Cells(Rows.Count, cVntColCountries).End(xlUp)).Value2

    ' Paste list of tabs from column cVntColTabs into array
    vntTabs = .Range(.Cells(cLngRow1, cVntColTabs), _
        .Cells(Rows.Count, cVntColTabs).End(xlUp)).Value2

  End With
  ' The data is in arrays instead of ranges.

  ' 1. According to the following line the workbook objWbMaster is already open.
  '    Is that true? yeah, but I moved the strpath up because I want it to be inputed once
  Set objWbMaster = Workbooks(cStrMaster)
        ' Determine the path to search for files in.         
          strPath = Application.InputBox(Prompt:=strMsg1, _
          Title:="InputBox Method", Type:=2) ' Type:=2 = text
  '
  For lngCountries = LBound(vntCountries) To UBound(vntCountries)
       If vntCountries(lngCountries, 1) <> "" And strPath <> "" Then

        strCountry = vntCountries(lngCountries, 1)


        ' When Cancel is clicked in Input Box ... Exit Sub
        If strPath = "False" Or IsError(strPath) Then
          MsgBox strMsg2
          Exit Sub
        End If

        If Right(strPath, 1) <> "\" Then strPath = strPath & "\"

        Application.ScreenUpdating = False

        strFile = Dir(strPath & cStrTemplate & strCountry & "*" & cStrExt)
        ' VBA Help: Dir returns the first file name that matches pathname. To
        '           get any additional file names that match pathname, call Dir
        '           again with no arguments. When no more file names match, Dir
        '           returns a zero-length string ("").
        ' i.e. The approach is correct!
        Do While strFile <> ""

          Set ojbWbEach = Workbooks.Open(strPath & strFile)

          ' 2. When would this ever happen?
          If Len(ojbWbEach.Name) <= 253 Then
            ' Add a new sheet with the file's name (remove the extension)
            With objWbMaster
              ' 3. Isn't the blnLastSheet always False. What should it be doing?
              Dim blnLastSheet As Boolean
              Dim intSheetsCounter As Integer
              Dim intSheets As Integer
              intSheets = .Worksheets.Count
              ' 4. Why parentheses in ... Step (-1)?
              For intSheetsCounter = intSheets To 1 Step -1
                ' 5. Why parentheses in (blnLastSheet)?
                If blnLastSheet = False Then
                  ' Place sheet at the end.
                  Set objWsNew = .Worksheets _
                      .Add(After:=.Worksheets(intSheetsCounter))
                  With objWsNew
                    .Cells(1, 1) = cStrNewHeader1
                    .Cells(1, 2) = cStrNewHeader2
                    .Cells(1, 3) = cStrNewHeader3

                  End With
                End If
              Next
            End With
           Else
            MsgBox strMsg3
            ojbWbEach.Close False
            GoTo Exit_Loop
          End If

          ' Loop through all worksheets in ojbWbEach.
          For Each objWsEach In ojbWbEach.Worksheets
            With objWsEach

              For lngTabs = LBound(vntTabs) To UBound(vntTabs)
                If .Name = vntTabs(lngTabs, 1) Then
' _
                    'And objWsNew.Name = strCountry
'
                  ' Get the first empty row in the new sheet
                      lngPasteRow = objWsNew.Cells.Find(What:="*", Lookat:=xlPart, _
                      LookIn:=xlFormulas, SearchOrder:=xlByRows, _
                      SearchDirection:=xlPrevious).row + 1

                  ' Pasting a range into a same sized range is much faster than
                  ' looping or copy/pasting.
                  objWsNew.Range(objWsNew.Cells(lngPasteRow, cVntFirstCol), _
                      objWsNew.Cells(cLngLastRow + lngPasteRow - cLngFirstRow, _
                      cVntLastCol)) = _
                  .Range(.Cells(cLngFirstRow, cVntFirstCol), _
                      .Cells(cLngLastRow, cVntLastCol)).Value2
                      objWsNew.Name = strCountry

                End If
              Next

            End With

          Next
        ojbWbEach.Close False
Exit_Loop:
          Set ojbWbEach = Nothing
          strFile = Dir
        Loop
        End If
    Next lngCountries

  Set objWsEach = Nothing
  Set objWsNew = Nothing
  Set ojbWbEach = Nothing
  Set objWbMaster = Nothing

  Call Module2.clean
  Application.ScreenUpdating = True

End Sub

它的作用是它还创建了额外的空白工作表,我必须使用 sub clean 进行清理。

【讨论】:

    【解决方案3】:

    这是来自我的整合商的代码,也许你可以得到一个想法。

       Dim lRow As Long
       Dim LastRow As Long
       lRow = Sheets("Sheet1").Cells(Rows.Count, "E").End(xlUp).Row
       lRow = lRow + 100
       LastRow = WorksheetFunction.Max(Sheets("Sheet1").Cells(Rows.Count, "E").End(xlUp).Row, 9)
       LastRow = LastRow + 1
       sht1.Range("A10:Q" & lRow).Copy
       sht2.Range("A" & LastRow).PasteSpecial
    
       Dim rowL As Long
       rowL = sht1.Range("E65536").End(xlUp).Row
       sht1.Range("B7").Copy Destination:=sht2.Range("R" & LastRow)
       sht1.Range("D7").Copy Destination:=sht2.Range("S" & LastRow)
    

    【讨论】:

    • 新的 Excel 版本有更多的行。这就是为什么使用 '"E" & Rows.Count' 来使代码在所有版本中都能正常工作。
    猜你喜欢
    • 2014-12-23
    • 1970-01-01
    • 2021-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-24
    • 1970-01-01
    相关资源
    最近更新 更多