VBA to sort table and ignore total row - vba

I have the range Table3 as shown below:
The rows are not fixed and could increase or decrease, I have thus created it as a table Table3 to accommodate this behavior and also so I could use it in a VBA as a ListObjects.
The VBA below is meant to sort the table, however because the Totals is part of the range, the sort doesn't work as intended.
Sub sort()
ActiveWorkbook.Worksheets("Project 2013").ListObjects("Table3").sort.SortFields _
.Clear
ActiveWorkbook.Worksheets("Project 2013").ListObjects("Table3").sort.SortFields _
.Add Key:=Range("Table3[Description3]"), SortOn:=xlSortOnValues, Order:= _
xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Project 2013").ListObjects("Table3").sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
Can someone please help modify the code to ignore the Totals row (i.e to include only the range below the header and above the Totals row) before applying the sort
EDIT
At the moment, this is my attempt at redefining a new range without the last row
Sub sort()
Dim resizedTable As ListObject
Set resizedTable = Sheets("Sheet1").ListObjects("Table1")
With resizedTable
.Resize .Range.Resize(.Range.Rows.Count - 1, .Range.Columns.Count)
End With
resizedTable.sort.SortFields.Clear
resizedTable.sort.SortFields _
.Add Key:=Range("resizedTable[Description]"), SortOn:=xlSortOnValues, Order:= _
xlAscending, DataOption:=xlSortNormal
   
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End Sub
Any help will be appreciated.

Set a new range for your table, just one row shorter » totalRowCount - 1.
Here, x is your input range
Set x = Range(x.Cells(1, 1), x.Cells(x.Rows.Count - 1, x.Columns.Count))
or use the resize method
Sub CutOffLastLine()
With ActiveWorkbook.Worksheets("Project 2013").ListObjects("Table3")
.Resize .Range.Resize(.Range.Rows.Count - 1, .Range.Columns.Count)
End With
End Sub

Related

Why isn't my VBA sorting my column data correctly?

I am trying to delete the first row of the Excel sheet and sort a specific column using its name "CUST_RELPO". I am using the column header name since the name may change.
Sorting and copying the column from the second row since I do need to copy the column header.
Sub ClearFirstRow()
'
' ClearFirstRow Macro
'
'
Rows("1:1").Select
Selection.Delete Shift:=xlUp
Cells.Select
Dim rngcustrelpo As Range
xindex = Application.ActiveCell.Column
Set rngcustrelpo = ActiveSheet.UsedRange.Find("CUST_RELPO")
If rngcustrelpo Is Nothing Then
MsgBox "CUST_RELPO column was not found."
Exit Sub
End If
'Cells.Select
Range(rngcustrelpo, rngcustrelpo.End(xlDown)).Select
ActiveWorkbook.Worksheets("BACKORDER").Sort.SortFields.Add Key:=ActiveSheet.UsedRange, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortTextAsNumbers
With ActiveWorkbook.Worksheets("BACKORDER").Sort
.SetRange ActiveSheet.UsedRange
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Set rngcustrelpo1 = rngcustrelpo.Offset(1, 0)
Range(rngcustrelpo1, rngcustrelpo1.End(xlDown)).Select
Selection.Copy
End Sub
However, it is not sorting the data like I am expecting. I am not sure what I am missing here.
Key:=ActiveSheet.UsedRange is a complete misunderstanding of the sort method. (Usedrange covers the whole used area on the sheet - often "empty" cells, too.) The same applies to .SetRange ActiveSheet.UsedRange. It is not bad just needless. SetRange is needed when you want to limit the area to be sorted. If you want to sort on only one key (column), then change this
ActiveWorkbook.Worksheets("BACKORDER").Sort.SortFields.Add Key:=ActiveSheet.UsedRange, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= xlSortTextAsNumbers
With ActiveWorkbook.Worksheets("BACKORDER").Sort
.SetRange ActiveSheet.UsedRange
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
to this:
With ActiveWorkbook.Worksheets("BACKORDER").Sort
.Key rngcustrelpo
.Header = xlYes
.MatchCase = False
.Order:=xlAscending
.Orientation = xlTopToBottom
.SortOn:=xlSortOnValues
.DataOption:= xlSortTextAsNumbers
.SortMethod = xlPinYin
.Apply
End With
And you can find more info here: Excel SortFields add then sort and here: Most efficient way to sort and sort syntax VBA

VBA: Referencing a Range stored as variable in a Sorting sub

Very novice programmer so hope my question isn't too dumb. I'm trying to set make a common sorting subroutine that I can call, but where the sorting subroutine looks at a variable for the range. This way I can set the range elsewhere (such as the button where I call the subroutine) and not have it hard-coded into the sorting routine.
So far my train of thought is this:
Dim Column As Range
Sub SortCodeDsc()
ActiveWorkbook.Worksheets("Benchmark Data").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Benchmark Data").Sort.SortFields.Add Key:=Range("A4:A100").Value, SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Benchmark Data").Sort
.SetRange Range("A4:HH100")
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
Private Sub Year_Click()
Set Column = ActiveSheet.Range("A4:A100")
Call SortCodeDsc
End Sub
So where Range("A4:A100") is in the Sub SortCodeDsc I would instead like to call variable Column which I Set to the range from A4 to A100. How do I properly call that variable when calling the subroutine?
It's probably not a good idea to use the word "Column" as a variable. That should be a saved word. Use Column1 instead and turn your sub into a function, passing it the range parameter.
Function SortCodeDsc(columnRange As Range)
ActiveWorkbook.Worksheets("Benchmark Data").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Benchmark Data").Sort.SortFields.Add Key:=columnRange, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Benchmark Data").Sort
.SetRange Range("A4:HH100")
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Function
Private Sub Year_Click()
Dim column1 As Range
Set column1 = ActiveWorkbook.Worksheets("Benchmark Data").Range("A1:A100")
Run SortCodeDsc(column1)
End Sub

Looping macro to work on all columns individually without writing long list of repeated transitions

I am trying to loop this macro (below) to go through all me data columns but need some help creating a loop or adjusting the current code to work for all columns.
Sub Trial_5()
'
ActiveCell.Offset(0, -7).Columns("A:A").EntireColumn.Select
ActiveWorkbook.Worksheets("Sheet6").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet6").Sort.SortFields.Add Key:=ActiveCell, _
SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Sheet6").Sort
.SetRange ActiveCell.Range("A1:A16395")
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
`enter code here`.SortMethod = xlPinYin
.Apply
End With
End Sub
do I adjust the .columns or the activecell.offset to total range??
adjusted script
Sub eachcolumndesending()
'
' eachcolumndesending Macro
' descending
'
'
Columns("A:A").Select
ActiveWorkbook.Worksheets("Sheet5").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet5").Sort.SortFields.Add Key:=Range("A1"), _
SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Sheet5").Sort
.SetRange Range("A2:A32")
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Columns("B:B").Select
ActiveWorkbook.Worksheets("Sheet5").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet5").Sort.SortFields.Add Key:=Range("B1"), _
SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Sheet5").Sort
.SetRange Range("B1:B33")
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
I suggest you move away from the recorded code to what VBA actually requires for a single column sort.
Sub sortAllColumns()
Dim c As Long
On Error Resume Next '<~~ may be necessary if a column breaks the sort
With Worksheets("Sheet5")
For c = .UsedRange.Columns.Count To 1 Step -1
With .Columns(c)
.Cells.Sort Key1:=.Columns(1), Order1:=xlDescending, _
Orientation:=xlTopToBottom, Header:=xlGuess
End With
Next c
End With
End Sub
Btw, you probably shouldn't have to xlGuess at the existence of a header. Either one is there (xlYes) or not (xlNo) but you know your data better than I.

Run Macro after OLAP Query

This is my Workbook:
Sheets layout:
Source: Pivot Table with Analysis Services connection
Distributors: Stuff to be sorted connected with the Pivot
Output: Chart based on sorted data of Distributors + Slicers connected to Pivot
What I need to do is: Launch Sorting Macro after each OLAP Query (= each time I use the slicers).
Sorting Code
Sub Sorting()
'This line finds the last occupied row in column A
'And you can use that LR variable in all the following Range Statements.
LR = Cells(Rows.Count, "C").End(xlUp).Row
ActiveWorkbook.Worksheets("Distributors").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Distributors").Sort.SortFields.Add Key:=Range("C4:C102" & LR) _
, SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Distributors").Sort
.SetRange Range("A3:C102" & LR)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
ActiveWorkbook.Worksheets("Distributors").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Distributors").Sort.SortFields.Add Key:=Range("D4:D102") _
, SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Distributors").Sort
.SetRange Range("D3:F102")
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
Solved. Launch Macro after each Pivot Update
Alt+F11, Right click on Source (Sheet with PivotTable)
View Code
Insert the Event trigger
Event Trigger
Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable)
'your_macro_here
End Sub
Keep in mind that you can't enter reference to a Module. Insert directly your macro.

Sorting multiple sheets - code clean up

Looking for some guidance on sorting columns across multiple sheets.
I have 2 data sets (tab1: ABC and tab2: XYZ).
I'm trying to sort both sheets (range column A to column J) by column A in descending order.
This is what I have so far... it is recorded. I would very much like to clean up my code and also look for better ways to approach sorting by columns. Any help/tips would be appreciated.
Sub sortingcolumns()
Application.Goto Reference:="ABC!A1"
ActiveWorkbook.Worksheets("ABC").sort.SortFields.Clear
ActiveWorkbook.Worksheets("ABC").sort.SortFields.Add Key:=Range("A1"), _
SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:= _
xlSortTextAsNumbers
With ActiveWorkbook.Worksheets("ABC").sort
.SetRange Range("A2:K187")
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Application.Goto Reference:="XYZ!RC"
ActiveWorkbook.Worksheets("XYZ").sort.SortFields.Clear
ActiveWorkbook.Worksheets("XYZ").sort.SortFields.Add Key:=Range("A1"), _
SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:= _
xlSortTextAsNumbers
With ActiveWorkbook.Worksheets("XYZ").sort
.SetRange Range("A2:J179")
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
Since you are using "with" you can combine those into one bigger with statement:
With ActiveWorkbook.Worksheets("ABC").sort
.SortFields.Clear
.SortFields.Add Key:=Range("A1"), _
SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:= _
xlSortTextAsNumbers
.SetRange Range("A2:K187")
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Also, you can remove the .header, .matchcase, .orientation, .sortmethod if you don't need to sort by those.
call sortingcolumns ActiveWorkbook.worksheets("ABC"), 2, 187
call sortingcolumns ActiveWorkbook.worksheets("XYZ"), 2, 179
Sub sortingcolumns(sht as Worksheet, First as Integer, Last as Integer)
With sht.sort
.sortfields.clear
.sortfields.add Key:=Range("A1"), SortOn:=xlSortOnValues, Order:=xlDescending, _
DataOption:=xlSortTextAsNumbers
.setrange = sht.range("A" & First & ":K" & Last)
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
You could modify it to provide the first and last columns, as well, then you have a nice utility function that can sort any range for you just by passing the appropriate parameters.
I like to fully qualify all objects e.g. ActiveWorkbook becomes Excel.ActiveWorkbook. This is a personal choice.
Also I like to avoid Active... objects so if this code only needs to act on the workbook where the code is stored then switch to ThisWorkbook
With Excel.ThisWorkbook.Worksheets("ABC").sort
.SortFields.Clear
.SortFields.Add _
Key:=Range("A1"), _
SortOn:=Excel.xlSortOnValues, _
Order:=Excel.xlDescending, _
DataOption:= Excel.xlSortTextAsNumbers
.SetRange Range("A2:K187")
.Header = Excel.xlNo
.MatchCase = False
.Orientation = Excel.xlTopToBottom
.SortMethod = Excel.xlPinYin
.Apply
End With
If it needs to act on a separate workbook then use an object variable. For example if the target columns are in a book called foo.xlsx (that we assume is open)
Dim myFooBk As Excel.workbook
Set myFooBk = Excel.workbooks("foo.xlsx")
With myFooBk.Worksheets("ABC").sort
.SortFields.Clear
.SortFields.Add _
Key:=Range("A1"), _
SortOn:=Excel.xlSortOnValues, _
Order:=Excel.xlDescending, _
DataOption:= Excel.xlSortTextAsNumbers
.SetRange Range("A2:K187")
.Header = Excel.xlNo
.MatchCase = False
.Orientation = Excel.xlTopToBottom
.SortMethod = Excel.xlPinYin
.Apply
End With
Also the with could be altered slightly by moving the .Sort inside the clause:
Dim myFooBk As Excel.workbook
Set myFooBk = Excel.workbooks("foo.xlsx")
With myFooBk.Worksheets("ABC")
.sort.SortFields.Clear
.sort.SortFields.Add _
Key:= .Range("A1"), _ '<<more specific now as `.` infront of Range
SortOn:=Excel.xlSortOnValues, _
Order:=Excel.xlDescending, _
DataOption:= Excel.xlSortTextAsNumbers
.sort.SetRange .Range("A2:K187") '<<more specific now as `.` infront of Range
.sort.Header = Excel.xlNo
.sort.MatchCase = False
.sort.Orientation = Excel.xlTopToBottom
.sort.SortMethod = Excel.xlPinYin
.sort.Apply
End With
Yes, you can shorten it substantially:
Sub sortingcolumns()
Worksheets("ABC").Range("A2:K187").sort key1:=Range("A1"), Order1:=xlDescending
Worksheets("XYZ").Range("A2:J179").sort key1:=Range("A1"), Order1:=xlDescending
End sub
I made the following changes:
I used the (old) range.sort construct instead of the (newer) worksheet.sort. With the old construct, you simply identify the range to sort and then apply the .sort method with the appropriate arguments. The newer one is also more flexible but also more complicated to use.
The only arguments you need are key1 and Order1. If you wanted to sort ascending, you could also eliminate the order1 argument; then you'd have just one argument.
If you don't specify an argument, Excel will use the default. All of the arguments in your snippet are the default arguments for the sort method. Therefore you don't need to specify them.