I received this Private Message from dfulmer:

Originally Posted by
dfulmer
Hi tigeravatar,
I have been trying to figure out a way to split data into separate sheets based on one criteria all day and then was given a link to your macro. It initially works great - everything sorted fine. However, my main sheet of data will constantly be updated and/or edited and when I re-ran the macro, it didn't move any updates that were made.
Any thoughts?
I looked over the code and realized that if the sheet name already exists, then it does not actually move the data to the existing sheet. This 3rd version addresses that issue:
Sub SplitDataIntoSheetsByCriteria_v3()
'Macro created by TigerAvatar at www.excelforum.com, November 2012
'Purpose is to split the data of a sheet into separate sheets based on a key column
'This is version 3, which fixes a bug encountered when a sheet name already exists
'Declare constants
'Adjust these to suit your specific needs
Const lHeaderRow As Long = 1 'The number of the Header Row
'Declare variables
Dim ws As Worksheet 'Used to loop through the worksheets in order to alphabetize them
Dim wsData As Worksheet 'Used to store the Data Sheet to a worksheet object variable
Dim wsDest As Worksheet 'Used to store the Destination Sheet to a worksheet object variable
Dim rngSelection As Range 'Used to store the selection chosen when the user is prompted to select the criteria column
Dim rngCriteria As Range 'Used to store the Range on the Data Sheet that will be evaluated
Dim CriteriaCell As Range 'Used to loop through rngCriteria
Dim lCalc As XlCalculation 'Used to store the current calculation state of the workbook
Dim strNameWS As String 'Used to create legal worksheet names
Dim strHistory As String 'Used to keep a criteria history so that multiple items do not get copied over
Dim i As Long 'Generic looping variable
'Prompt user to select the criteria column
On Error Resume Next 'Prevents error if user pressed cancel
Set rngSelection = Application.InputBox("Select the criteria column that will be evaluated", "Criteria Column Selection", Selection.Address, Type:=8)
On Error GoTo 0 'Removes the On Error Resume Next behavior
If rngSelection Is Nothing Then Exit Sub 'User pressed cancel
'Set the wsData and rngCriteria variables using the header row constant and the rngSelection that was chosen by the user
Set wsData = rngSelection.Parent
Set rngCriteria = Range(wsData.Cells(lHeaderRow, rngSelection.Column), wsData.Cells(Rows.Count, rngSelection.Column).End(xlUp))
'Store the current calculation state, set calculation to manual, disable events, alerts, and screenupdating
'This allows the code to run faster and prevents "screen flickering"
With Application
lCalc = .Calculation
.Calculation = xlCalculationManual
.EnableEvents = False
.DisplayAlerts = False
.ScreenUpdating = False
End With
'Assume code will fail and provide an error handler
On Error GoTo CleanExit
'Loop through each cell in rngCriteria
For Each CriteriaCell In rngCriteria.Cells
'Make sure the cell is after the Header Row
If CriteriaCell.Row > lHeaderRow Then
'Make sure the cell is not blank
If Len(CriteriaCell.Text) > 0 Then
'Check if the text in this cell has not been encountered yet
If InStr(1, "||" & strHistory & "||", "||" & CriteriaCell.Text & "||", vbTextCompare) = 0 Then
'The text in this cell has not been encountered yet, add it to the history list and proceed with data split
strHistory = strHistory & "||" & CriteriaCell.Text
'Generate a legal worksheet name
strNameWS = CriteriaCell.Text
For i = 1 To 7
strNameWS = Replace(strNameWS, Mid(":\/?*[]", i, 1), " ")
Next i
strNameWS = Trim(Left(WorksheetFunction.Trim(strNameWS), 31))
'Check if there is already a sheet with the same name
If Not Evaluate("IsRef(" & strNameWS & "!A1)") Then
'Need to create a new sheet
'Add the new sheet, name it appropriately, copy over the headers
Set wsDest = Sheets.Add(After:=Sheets(Sheets.Count))
With wsDest
.Name = strNameWS
wsData.Rows(lHeaderRow).EntireRow.Copy .Range("A1")
End With
Else
'Sheet already exists
Set wsDest = Sheets(strNameWS)
End If
'The following line is used to clear existing data in the destination sheet to prevent duplicates
'Put an apostrophe in front of the line to comment it out so that it will not run if you do not need/want it to
wsDest.UsedRange.Offset(1).ClearContents
'Copy over the data
rngCriteria.AutoFilter 1, CriteriaCell.Text
rngCriteria.Offset(1).EntireRow.Copy wsDest.Cells(Rows.Count, "A").End(xlUp).Offset(1)
End If
End If
End If
Next CriteriaCell
'Alphabetize the worksheets
For Each ws In ActiveWorkbook.Sheets
For i = 1 To ActiveWorkbook.Sheets.Count
If ws.Name < Sheets(i).Name Then ws.Move Before:=Sheets(i)
Next i
Next ws
'Set the Data sheet to be the first sheet in the workbook and select that sheet so it is displayed after macro completes
wsData.Move Before:=Sheets(1)
wsData.Select
'If there were any errors, the code immediately goes here
'The code will also exit here even if there are no errors
CleanExit:
'Remove any remaining filters
rngCriteria.AutoFilter
'Set calculation back to what it was, re-enable events, alerts, and screenupdating
With Application
.Calculation = lCalc
.EnableEvents = True
.DisplayAlerts = True
.ScreenUpdating = True
End With
'Display the error that occurred (if any) and clear the error
If Err.Number <> 0 Then
MsgBox Err.Description, , "Error: " & Err.Number
Err.Clear
End If
'Object variable cleanup
Set ws = Nothing
Set wsData = Nothing
Set wsDest = Nothing
Set rngSelection = Nothing
Set rngCriteria = Nothing
Set CriteriaCell = Nothing
End Sub
Thank you for pointing that, dfulmer!
Bookmarks