EXCEL VBA Sort after Populating Data - vba

I am new to VBA, and excel macros, but not basic programming. I have a few dozen excel files, that I am taking data from, cleaning it, and populating it into one file. After the data is populated, I'd like to sort it according to Column A. After an 2 hours of playing with it, I just recorded a macro and cut and pasted it into my ButtonCall sub. But I'd like to know why its working and why the solutions I found here, and online would not work for me...
Why does this simple code NOT work:
Set q = ThisWorkbook.Worksheets(2)
LastRow = q.UsedRange.rows.Count 'q.UsedRange.Row ' - 1 + q.UsedRange.rows.Count
LastCol = q.UsedRange.Columns.Count
q.Range("A6:AAA" & LastRow).Sort Key:=q.Columns("A"), Order:=xlDescending
While this modified recorded Macro does?
Set q = ThisWorkbook.Worksheets(2)
LastRow = q.UsedRange.rows.Count 'q.UsedRange.Row ' - 1 + q.UsedRange.rows.Count
LastCol = q.UsedRange.Columns.Count
q.Sort.SortFields.Clear
q.Sort.SortFields.Add Key:=Range("A6:A" & LastRow), SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
With q.Sort
.SetRange Range("A6:AAA" & LastRow)
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Any thoughts? Thanks.

Your code is using the range.sort method, while the original code is employing the sort object - two different things.
This code will sort "A6" to end of data by column A, using the Range.Sort method.
Sub MySort()
Dim q As Worksheet
Dim r As Range
Set q = ThisWorkbook.Worksheets(2)
' specify data range from "A6" to end of data
Set r = q.Range("A6", q.Cells.SpecialCells(xlCellTypeLastCell))
' Header:=xlNo assumes A6 row is included in data to be sorted
r.Sort key1:=r(1, 1), Order1:=xlDescending, Header:=xlNo
End Sub

Related

excel vba: Can someone help me understand how to use a cusom sort order?

I've been working on a code to sort a block of data using Range.Sort using a custom sort order. I've tried to record macros and look online but came up more confused about this problem.
For the key1:= argument; is it a single cell (e.g. Range("A1")) or a whole column?
How exactly can I use a custom sort order in OrderCustom:=?
In the case I'm going about this all wrong; the Range in Range.Sort can be anywhere as long as its in one continuous block, correct?
Here is the code I'm working with:
Sub Test()
Dim quantity As Variant
quantity = 2 + WorkshetFunction.Count(Range("A" & 3, "K" & 900))
With Range("A" & 3, "K" & quantity)
.Sort key1:=Range("A" & 3)
Order:=xlAscending
Header:=xlNo
OrderCustom:="VALID, GOOD, DUE, OVERDUE, WAY OVERDUE, MISSING"
'> This is the order in which I want the items on this list sorted by.
End With
End Sub
The Range("A" & 3, "K" & quantity) refers to a block of data containing on "A" the "status" of some items determined by their calibration expiration dates, amongst other data which is irrelevant for this purpose, and I'm not in the liberty to share. All I'm asking is help understanding the inner workings of the .Sort method. Thanks!
Give'r
Sub SortItOut()
Dim rng As Range, sh As Worksheet
Set sh = Sheets("Sheet1")
With sh
Set rng = .Range("A3:K" & .Cells(.Rows.Count, "K").End(xlUp).Row)
With rng
sh.Sort.SortFields.Clear
sh.Sort.SortFields.Add Key:=Range("A3") _
, SortOn:=xlSortOnValues, Order:=xlAscending, CustomOrder:= _
"VALID,GOOD,DUE,OVERDUE,WAY OVERDUE,MISSING", DataOption:=xlSortNormal
End With
With .Sort
.SetRange rng
.Orientation = xlTopToBottom
.Apply
End With
End With
End Sub

Find an empty cell and write something in another column in a huge file

I have huge files (over 500,000 rows) and I need to find if the "M" column is equal to "", if it is equal to "", than I need to write down something in the "N" column.
I do it with = to "" because IsEmpty() isn't working in those files (not sure why).
This is my current code: (I removed what wasn't necessary)
Sub sbVBA_COMMENTS_ExcelSheets()
'CONSTANTS SERVICENTRE FILES (PROD FOUR)
Const SC_STRLINE As Integer = 4 'FIRST PART IS ALWAYS AT LINE 4
Const SC_COLNUM As String = "B" 'PART NUMBERS WILL ALWAYS BE IN THE B COLUM FOR EVERY EXCEL FILES
Const SC_COLMKT As String = "K" 'NEW MARKETING CODE WILL ALWAYS BE IN THE K COLUM FOR EVERY EXCEL FILES
Const SC_COLDCT As String = "M" 'NEW DISCOUNT CODE WILL ALWAYS BE IN THE M COLUM FOR EVERY EXCEL FILES
Const SC_COLPRB As String = "N" 'NEW COMMENTS AND PROBLEM CODE WILL ALWAYS BE IN THE M COLUM FOR EVERY EXCEL FILES
'VARIABLES
Dim RowCount As Long
Dim ct As Long
'SET VARIABLES
RowCount = 0
ct = SC_STRLINE
Sheets(4).Select
'ADD COMMENTS
With Sheets(4)
RowCount = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
While ct <= RowCount
If Sheets(4).Range(SC_COLDCT & ct).Value = "" Then
Sheets(4).Range(SC_COLPRB & ct).Value = "EMPTY"
End If
ct = ct + 1
Wend
'FILTER BY COMMENTS
ActiveWorkbook.Worksheets(4).AutoFilter.Sort.SortFields.Clear
ActiveWorkbook.Worksheets(4).AutoFilter.Sort.SortFields.Add Key:= _
Range(SC_COLPRB & (SC_STRLINE - 1)), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("PROD FOUR").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
This is working... somehow... but it's SUPER SLOW and when I run the macro, my Excel dies almost 70% of the time.
Also sometimes, this macro will cause a problem where it will write down EMPTY everywhere for some reason...
Basically, i'm looking for a faster and cleaner way to do the same thing.
Is there a way to gain performance while doing the same thing?
Note that I'm not a coder, so if you could answer me in a way I can understand, that would be appreciated.
Try using the below code. Instead of dealing with a range, this converts the range into an array which will hopefully be faster.
Sub sbVBA_COMMENTS_ExcelSheets()
'CONSTANTS SERVICENTRE FILES (PROD FOUR)
Const SC_STRLINE As Integer = 4 'FIRST PART IS ALWAYS AT LINE 4
Const SC_COLNUM As String = "B" 'PART NUMBERS WILL ALWAYS BE IN THE B COLUM FOR EVERY EXCEL FILES
Const SC_COLMKT As String = "K" 'NEW MARKETING CODE WILL ALWAYS BE IN THE K COLUM FOR EVERY EXCEL FILES
Const SC_COLDCT As String = "M" 'NEW DISCOUNT CODE WILL ALWAYS BE IN THE M COLUM FOR EVERY EXCEL FILES
Const SC_COLPRB As String = "N" 'NEW COMMENTS AND PROBLEM CODE WILL ALWAYS BE IN THE M COLUM FOR EVERY EXCEL FILES
'VARIABLES
Dim RowCount As Long
Dim varray As Variant
Dim i As Long
'SET VARIABLES
RowCount = 0
ct = SC_STRLINE
'disable unnecessary hindrances
Application.ScreenUpdating = False
Application.Calculation = xlManual
With ThisWorkbook.Sheets(4)
'find last row
RowCount = .Cells(.Rows.Count, "A").End(xlUp).Row
varray = Range(SC_COLDCT & SC_STRLINE & ":" & SC_COLDCT & RowCount).Value
For i = UBound(varray, 1) To LBound(varray, 1) Step -1
If varray(i, 1) = "" Then 'if nothing
.Range(SC_COLPRB & i + 4).Value = "EMPTY"
End If
Next
'FILTER BY COMMENTS
.AutoFilter.Sort.SortFields.Clear
.AutoFilter.Sort.SortFields.Add Key:= _
Range(SC_COLPRB & (SC_STRLINE - 1)), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
End With
With ActiveWorkbook.Worksheets("PROD FOUR").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
The simple code below accomplishes what you want quickly. You can also use "" instead of vbNullString.
Dim i As Long
Dim lR As Long
lR = Cells(Rows.Count, "A").End(xlUp).Row
For i = 2 To lR
If Cells(i, "M").Value = vbNullString Then
Cells(i, "N").Value = "Empty"
End If
Next i

VBA sort code works for one sheet only but not multiple ones

When I run this code over multiple sheets I get a "Run-Time Error 1004:The sort reference is not valid. Make sure that it's within the data that you want to sort, and the first Sort By box isn't the same or blank":
Dim i As Long
For i = 6 To Worksheets.Count
'more code here
Dim ranged As range
Dim lRow As Long
With ThisWorkbook.Sheets(i)
lRow = .range("AJ" & .Rows.Count).End(xlUp).Row
Set ranged = .range("AJ2:AJ" & lRow)
.Sort.SortFields.Add Key:=ranged, _
SortOn:=xlSortOnValues, _
Order:=xlDescending, _
DataOption:=xlSortNormal
With .Sort
.SetRange ranged
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End With
'more code here
next i
I'm not sure why this doesn't work, but what is particularly baffling is that when I try to run it on one sheet only by changing With ThisWorkbook.Sheets(i) to With ThisWorkbook.Sheets("Sheetname") it works fine, but when I put it into the above structure to run over multiple ones it doesn't.The idea is to reverse the order of column AJ starting from AJ2 to the last row with data in it and the length of AJ is different in different sheets. Any ideas?
The way you are going about getting your sheetnames is the problem.
You are starting to iterate through i from 6 to Worksheets.count.. With this, assuming you have 10 sheets, you will only iterate through 4 i's before being complete.
The main issue though is that you probably don't have sheetnames that are Sheets(i). i is a variable here and furthermore, it isn't a string. So you need to have a string at the other end of the variable you are placing inside Sheets().
I think what you are trying to accomplish is using a LIST of sheet names that are Strings. Using the variable i as a Long or Integer to iterate through them. "i" being the NUMBER and name(i) being a String.
'This example assumes you have 6 sheets. Declare this at the top with your initial variable declarations.
Dim name(1 to 6) As String
name(1) = "Sheet1"
name(2) = "TotalSales"
name(3) = "MonthlyRevenue"
name(4) = "Sheet 4 Name"
name(5) = "Whatever This sheet is called"
name(6) = "Last Sheet Name"
or whatever your sheets are named. Then when you call Sheets you will use something more like this.
For i = 1 to 6
'YOUR CODE THAT YOU ALREADY ENTERED THAT WORKS WHEN THE SHEET NAME IS CORRECT
With ThisWorkbook.Sheets(name(i))
Next i

Sort rows by a specific column containing numbers

How to sort rows by a column containing numbers in ascending or descending order?
I know how to sort using Filters and Using Sort function in VBA. But it sorts in alphabetical order only not by numbers.
This is so far I have done.But still the sorting is coming alphabetically.
Sub sortdata()
Dim LastRow As Integer
NoOfRows = Sheets("RawData").Range("A" & Rows.Count).End(xlUp).Row
Sheets("RawData").Rows("2:" & NoOfRows).NumberFormat = "0"
Sheets("RawData").Sort.SortFields.Add Key:=Range("A1"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Sheets("RawData").Sort
.SetRange Range("A1:B" & NoOfRows)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
I prefer the Range.Sort method:
[EDIT 2]:
I have added the .TextToColumns line to programmatically address the numbers stored as text issue
Sub sortdata()
Dim ws As Worksheet
Set ws = Sheets("RawData")
With ws.Range("A1:B" & ws.Cells(Rows.Count, "A").End(xlUp).Row)
Intersect(.Cells, ws.Columns("A")).TextToColumns
.Sort Intersect(.Cells, ws.Columns("A")), xlAscending, Header:=xlGuess
End With
End Sub
[EDIT]:
After reading asker's comment:
I will provide an example . If the column A contains this
values:1,2,55,12,14,5343,22222,9 Then after sorting using filter or
inbuilt sort method. The values are sorted as
follows:1,12,14,2,22222,5343,9. But I need the result as follows:
1,2,9,12,14,5334,22222. Is there any in-built function for this?
The problem is that your numbers are stored as text. You'll need to convert them to numbers.
Select column A -> Data -> Text to Columns -> Finish
Now the numbers should sort correctly.

How to combine reverse-chronological sheets while rearranging cells, preventing stacking?

OK, I will try to explain this well. An odd problem to solve, and it's WAY beyond my skill level (novice).
Excel 2011 for Mac.
The workbook has 11 sheets.
Sheet names are all 'Month Year'. (Eg: sheet 1 is titled "June 2013")
Sheet names are reverse chronological. (June 2013, May 2013, April 2013 etc)
Each sheet has data in the same layout:
A1 is the sheet's name. B1 through to a varying endpoint on B hold dates. (approx two weeks but varies greatly between sheets)
A2 and downward in A is all names, as "last,first".
The remaining columns below B1 and outward are either blank, 0's, or 1's (attendance for date at row 1).
What I need to do:
Get ALL of the data in one sheet, with dates in chronological order along row 1 and names in alphabetical order down column A. Without messing up the associations between the names and the 0/1/blank values that existed on the original sheet.
What I have done:
I did this manually using the Excel GUI on a very similar sheet. It took forever!
I also have been writing or sourcing Subs to do some of the other work needed to these sheets to get them ready for this big rearranging. But I am already at my limits writing super simple "find rows with the word 'total' in them" sorts of stuff.
I know WHAT to do here, but have no clue HOW.
Start with the oldest sheet, copy into a new sheet(YearSheet).
Take names from 2ndOldest, paste into A under names already there.
Take dates and the cells beneath them into YearSheet, but staggered out on the columns so they begin where the first sheet left off.
Repeat again with nextYoungest, same deal. Names under names, dates and data shoved out along the letter axis to prevent overlap with prior dates.
Eventually it's a long list of names in A, and a descending step-pattern of data blocks in the remainder.
Sort it all by A, alphabetically.
Find and compress identical names into one row, without losing the data along the way (Why does Excel only keep top left? Aaargh!)
So, I know that's a lot to ask here.
Have no idea if this is too much or over the top for a question, but I am just stumped so am posting it in hopes somebody can make sense of the VBA to do it.
I created a workbook based on your description to use as sample data.
I wrote this macro
Sub Main()
Dim CombinedData As Variant
Dim TotalCols As Integer
Dim TotalRows As Long
Dim PasteCol As Integer
Dim PasteRow As Long
Dim i As Integer
Dim PivSheet As Worksheet
ThisWorkbook.Sheets.Add Sheet1
On Error GoTo SheetExists
ActiveSheet.Name = "Combined"
On Error GoTo 0
Range("A1").Value = "Name"
For i = ThisWorkbook.Sheets.Count To 1 Step -1
If Sheets(i).Name <> "Combined" Then
Sheets(i).Select
TotalCols = Sheets(i).Columns(Columns.Count).End(xlToLeft).Column
TotalRows = Sheets(i).Rows(Rows.Count).End(xlUp).Row
PasteCol = PasteCol + TotalCols - 1
If PasteRow = 0 Then
PasteRow = 2
Else
PasteRow = PasteRow + TotalRows - 1
End If
'Copy Date Headers
Range(Cells(1, 2), Cells(1, TotalCols)).Copy Destination:=Sheets("Combined").Cells(1, PasteCol)
'Copy Names
Range(Cells(2, 1), Cells(TotalRows, 1)).Copy Destination:=Sheets("Combined").Cells(PasteRow, 1)
'Copy Data
Range(Cells(2, 2), Cells(TotalRows, TotalCols)).Copy Destination:=Sheets("Combined").Cells(PasteRow, PasteCol)
End If
Next
Sheets("Combined").Select
ActiveSheet.Columns.AutoFit
With ActiveSheet.Sort
.SortFields.Clear
.SortFields.Add Key:=Range("A1"), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortNormal
.SetRange ActiveSheet.UsedRange
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Set PivSheet = Sheets.Add
ThisWorkbook.PivotCaches.Create(SourceType:=xlDatabase, _
SourceData:=Sheets("Combined").UsedRange, _
Version:=xlPivotTableVersion14).CreatePivotTable _
TableDestination:=PivSheet.Range("A1"), _
TableName:="PivotTable1", _
DefaultVersion:=xlPivotTableVersion14
For i = 1 To PivSheet.PivotTables("PivotTable1").PivotFields.Count
With ActiveSheet.PivotTables("PivotTable1")
If i = 1 Then
.PivotFields(i).Orientation = xlRowField
.PivotFields(i).Position = 1
Else
ActiveSheet.PivotTables("PivotTable1").AddDataField .PivotFields(i), _
"Sum of " & .PivotFields(i).Name, _
xlSum
End If
End With
Next
Application.DisplayAlerts = False
Sheets("Combined").Delete
Application.DisplayAlerts = True
PivSheet.Name = "Combined"
CombinedData = ActiveSheet.UsedRange
Cells.Delete
Range(Cells(1, 1), Cells(UBound(CombinedData), UBound(CombinedData, 2))).Value = CombinedData
Range("A1").Value = "Name"
Range(Cells(1, 1), Cells(1, UBound(CombinedData, 2))).Replace "Sum of ", ""
Columns.AutoFit
Exit Sub
SheetExists:
Application.DisplayAlerts = False
Sheets("Combined").Delete
Application.DisplayAlerts = True
Resume
End Sub
Which produces this result:
This was written in Excel 2010 in windows. I don't know what the differences are between the pc and mac versions but this may work for you.