dimanche 12 juin 2016

Splitting and saving Excel spreadsheets via VBA

I'm trying to write a macro for Excel which will take data in any number of columns and split it into a specified number of rows per sheet, with a separate prompt asking if I'd like to back up the sheets as separate files. What I've written works, but it's painfully inefficient for anything more than a couple hundred rows. Could someone please give me some pointers?

Private Sub ButtonOK_Click()

' Make sure the UserForm is completely filled in
If OptionYES.Value = False And OptionNO.Value = False Then
  MsgBox ("Please select if there is a header or not.")
  Exit Sub
End If
If TextNUMROWS.Value = "" Then
  MsgBox ("Please enter the number of cells you would like in each sheet.")
  Exit Sub
End If
If ComboBoxFileType.ListIndex = -1 Then
  MsgBox ("Please select if you would like backup files of the sheets to be created.")
  Exit Sub
End If



Dim SheetName As String
Dim FinalRow As Double, NumSheets As Double
Dim NextSheet As Integer

SheetName = ActiveSheet.Name
If OptionNO.Value = True Then
  NextSheet = TextNUMROWS - 1
Else
  NextSheet = TextNUMROWS
End If

' Get "Header?" value
If OptionYES.Value = True Then
  FinalRow = Cells(Rows.Count, 1).End(xlUp).Row - 1
Else
  FinalRow = Cells(Rows.Count, 1).End(xlUp).Row
End If

NumSheets = WorksheetFunction.Ceiling(FinalRow / TextNUMROWS, 1)

If NumSheets > 20 Then
  MsgBox ("The number of subsheets exceeds 20. Please reconfigure your data.")
  Exit Sub
End If

' Create new sheets with/without headers
For Iter1 = 1 To NumSheets
  Sheets.Add.Name = SheetName & "_sp" & Iter1
  If OptionYES.Value = True Then
    Worksheets(SheetName).Rows(1).EntireRow.Copy
    With Sheets(SheetName & "_sp" & Iter1)
      .Range("A" & .UsedRange.Rows.Count).PasteSpecial
    End With
  End If
Next Iter1

' Copy and paste data to newly created sheets
For Iter2 = 1 To NumSheets
  If OptionNO.Value = True Then
    Worksheets(SheetName).Rows(((Iter2 - 1) * TextNUMROWS) + 1).EntireRow.Copy
    With Sheets(SheetName & "_sp" & Iter2)
      .Range("A1").PasteSpecial
    End With
  End If
  For Iter3 = 1 To NextSheet
    Worksheets(SheetName).Rows(((Iter2 - 1) * TextNUMROWS) + Iter3 + 1).EntireRow.Copy
    With Sheets(SheetName & "_sp" & Iter2)
      .Range("A" & .UsedRange.Rows.Count + 1).PasteSpecial
    End With
  Next Iter3
  Sheets(SheetName & "_sp" & Iter2).Activate
  ActiveSheet.Cells(1, 1).Select
Next Iter2




'Sort lists alphabetically
  Dim N As Integer
  Dim M As Integer
  Dim FirstWSToSort As Integer
  Dim LastWSToSort As Integer
  Dim SortDescending As Boolean

  SortDescending = False

  If ActiveWindow.SelectedSheets.Count = 1 Then

  'Change the 1 to the worksheet you want sorted first
    FirstWSToSort = 1
    LastWSToSort = Worksheets.Count
  Else
    With ActiveWindow.SelectedSheets
      For N = 2 To .Count
        If .Item(N - 1).Index <> .Item(N).Index - 1 Then
          MsgBox "You cannot sort non-adjacent sheets"
          Exit Sub
        End If
      Next N
      FirstWSToSort = .Item(1).Index
      LastWSToSort = .Item(.Count).Index
    End With
    End If

  For M = FirstWSToSort To LastWSToSort
    For N = M To LastWSToSort
    If SortDescending = True Then
      If UCase(Worksheets(N).Name) > UCase(Worksheets(M).Name) Then
        Worksheets(N).Move Before:=Worksheets(M)
      End If
    Else
      If UCase(Worksheets(N).Name) < UCase(Worksheets(M).Name) Then
        Worksheets(N).Move Before:=Worksheets(M)
      End If
    End If
  Next N
Next M




'Create sheet backup files
Select Case ComboBoxFileType.ListIndex
  Case Is = 0
    FileType = ".xlsx"
  Case Is = 1
    FileType = ".xls"
  Case Is = 2
    FileType = ".csv"
End Select

If ComboBoxFileType.ListIndex <> 3 Then
  Dim xPath As String
  xPath = Application.ActiveWorkbook.Path
  Application.ScreenUpdating = False
  Application.DisplayAlerts = False
  For Each xWs In ThisWorkbook.Sheets
      xWs.Copy
      Application.ActiveWorkbook.SaveAs Filename:=xPath & "" & xWs.Name & FileType
      Application.ActiveWorkbook.Close False
  Next
  Application.DisplayAlerts = True
  Application.ScreenUpdating = True
  MsgBox ("Done. Data has been split into " & NumSheets & " sheets and saved as file type " & FileType & ".")

Else
  MsgBox ("Done. Data has been split into " & NumSheets & " sheets.")
End If

Unload Me

End Sub

Private Sub ButtonCANCEL_Click()

  Unload Me

End Sub

Private Sub UserForm_Initialize()
  With Me.ComboBoxFileType
    .AddItem "Yes, save as .xlsx."
    .AddItem "Yes, save as .xls."
    .AddItem "Yes, save as .csv."
    .AddItem "No, do not save sheets."
  End With
End Sub

I apologize for the ugly code, I'm learning the language on my own via Google, so what you see here is a Frankenstein of some other things I've found that I've tweaked slightly to make work. As I said, it works as-is, but I'd really love for it to be more efficient, as it takes 10s of minutes for thousands of lines of data and becomes less efficient than manually splitting the sheets.

Aucun commentaire:

Enregistrer un commentaire