SUMIF as a macro in Excel (VBA) - vba

So this is the concept I'm working with.
I have Sheet1 with many keys and values on it:
Then on sheet2 I have been using a SUMIF function to work out the total values from sheet1:
This is just an example and the actual datasets are much larger. I need to design a macro that will automatically generate and insert the SUMIF formula into the correct cells in sheet2. Can anyone think of a way to do this?

Even without knowing any other requirements or what you're doing or how many columns or keys there are or anything else, you can:
record a macro,
assign it to a button,
write one line of code so
that when user clicks button it will run macro on the column
selected (or when first cell of column is selected).
If there are 100+ columns then yea it's tedious and you'd want a macro to loop through it all but I have no idea what you got/need.

Here is a solution.
With [sheet1!a1:index(sheet1!a:a,count(sheet1!a:a))]
[b1:index(sheet2!b:b,count(sheet2!a:a))].Offset(1).Formula = _
"=sumif(sheet1!" & .Offset(1).Address & ",a2,sheet1!" & .Offset(1, 1).Address & ")"
End With
This assumes that the column A on sheet2 is already in place. Likewise it assumes that the Header for column B on sheet2 is already in place, and that the rest of column B is blank and will be filled by the above code.
It also assumes numeric keys.
This solution can easily be adjusted if any assumptions are wrong. Just let me know.

I would read the data up from sheet one and then build the second sheet. You will need to add a reference for the adodb recordset. In the VBA IDE on the tools pulldown menu select references. Select "Microsoft ActiveX Data Objects 2.8 Library".
Private Sub CommandButton10_Click()
Dim rs As New ADODB.Recordset
Dim ws As Excel.Worksheet
Dim lRow As Long
Dim lLastRowSheet1 As Long
Set ws = ActiveWorkbook.Sheets("Sheet1")
'Add fields to your recordset for storing data.
With rs
.Fields.Append "Row", adInteger
.Fields.Append "Key", adInteger
.Fields.Append "Val", adInteger
.Open
End With
lLastRowSheet1 = ws.UsedRange.Rows.count
lRow = 1
'Loop through and record what is in the columns
Do While lRow <= ws.UsedRange.Rows.count
rs.AddNew
rs.Fields("Row").Value = lRow
rs.Fields("Key").Value = ws.Range("A" & lRow).Value
rs.Fields("Val").Value = ws.Range("B" & lRow).Value
rs.Update
lRow = lRow + 1
ws.Range("A" & lRow).Activate
Loop
If rs.EOF = False Then
rs.MoveFirst
End If
'Switch to the second worksheet
Set ws = Nothing
Set ws = ActiveWorkbook.Sheets("Sheet2")
'Now go through the data from sheet one and build the list of keys
Dim iLastKey As Integer
lRow = 1
Do While rs.EOF = False
'For each unique key add a row to the second sheet.
If rs.Fields("Key").Value <> iLastKey Then
ws.Range("A" & lRow).Value = rs.Fields("Key").Value
ws.Range("B" & lRow).Formula = "=sumif(sheet1!$A$2:$A$" & lLastRowSheet1 & ",A" & lRow & ",Sheet1!$B$2:$B$" & lLastRowSheet1 & ")"
lRow = lRow + 1
End If
iLastKey = rs.Fields("Key").Value
rs.MoveNext
Loop
End Sub

This is what I used in the end:
Sub GetKeyVals()
' GetKeyVals Macro
' Get the key values based on the Unique customer codes
' Define sheet
Dim Extract As Worksheet
Set Extract = ActiveSheet
'Define lastRow
Dim lastRow As Long
lastRow = Extract.Cells(Rows.Count, "A").End(xlUp).row
' Loop round all rows
Dim n As Long
For n = 2 To lastRow
Cells(n, 3).FormulaR1C1 = _
"=SUMIF(SAPDump!R2C8:R1317C8,Extract!RC[-1],SAPDump!R2C10:R1317C10)*-1"
Range("C3").Select
Next n
' Insert Title
Dim Txt As Range
Set Txt = ActiveSheet.Range("C1")
Txt.Value = "KeyValue"
Txt.Font.Bold = True
End Sub
The problem is it's really slow, does anyone know a way of making this run any faster? cheers

Related

MS Excel VBA - Looping through columns and rows

Hello stackoverflow community,
I must confess I primarily code within MS Access and have very limited experience of MS Excel VBA.
My current objective is this, I have an "Expense Report" being sent to me with deductions, this report has many columns with different account names that may be populated or may be null.
My first step will be to start on the first record (Row 14; Column A-K contains personal info regarding the deduction) then skip to the first deduction account (deduction accounts start at column L and span to column DG) checking if each cell is null, if it is then keep moving right,If there is a value present, I need to copy it into an external workbook "Payroll Template" starting at row 2 (Column J for the deduction itself), as well as copy some personal info from the original row in the "Expense Report" related to that deduction (currRow: Column C,E,F from "Expense Report" to "Payroll Template" Columns B,C,D).
Then move to the right until the next cell contains a value, and repeat this process on a new row in the "Payroll Template". Once the last column (DG) has been executed I want to move to the next row (row 15) and start the process again all the way until the "LastRow" in my "Used Range".
I greatly appreciate any feedback, explanations, or links that may point me towards my goal. Thank you in advance for taking the time to read though this!
Current state of code:
`< Sub LoadIntoPayrollTemplate()
Dim rng As Range
Dim currRow As Integer
Dim UsedRng As Range
Dim LastRow As Long
Set UsedRng = ActiveSheet.UsedRange
currRow = 14
Set wb = ActiveWorkbook '"Expense Report"
Set wb2 = MyFilepath '"Payroll Template"
'Copied from another procedure, trying to use as reference
LastRow = rng(rng.Cells.Count).Row
Range("A14").Select
Do Until ActiveCell.Row = LastRow + 1
If (ActiveCell.Value) <> prev Then
currRow = currRow + 1
End If
ActiveCell.Offset(1, 0).Select
Loop
With Worksheets("Collections")
lstRow = .Cells(.Rows.Count, 1).End(xlUp).Row
Set rng = .Range(.Cells(14, 12), Cells(lstRow, 111))
End With
End Sub>`
The following code may do what you are after:
Sub LoadIntoPayrollTemplate()
Dim currRowIn As Long
Dim currColIn As Long
Dim currRowOut As Long
Dim wb As Workbook
Dim wb2 As Workbook
Set wb = ActiveWorkbook '"Expense Report"
Set wb2 = Workbooks.Open(Filename:=MyFilepath & "\" & "Payroll Template.xlsx")
'or perhaps
'Set wb2 = Workbooks.Open(Filename:=wb.path & "\" & "Payroll Template.xlsx")
With wb.ActiveSheet
currRowOut = 1
For currRowIn = 14 To .UsedRange.Row + .UsedRange.Rows.Count - 1
For currColIn = 12 To 111
If Not IsEmpty(.Cells(currRowIn, currColIn)) Then
currRowOut = currRowOut + 1
'I'm not sure which worksheet you want to write the output to
'so I have just written it to the first one in Payroll Template
wb2.Worksheets(1).Cells(currRowOut, "J").Value = .Cells(currRowIn, currColIn).Value
wb2.Worksheets(1).Cells(currRowOut, "B").Value = .Cells(currRowIn, "C").Value
wb2.Worksheets(1).Cells(currRowOut, "C").Value = .Cells(currRowIn, "E").Value
wb2.Worksheets(1).Cells(currRowOut, "D").Value = .Cells(currRowIn, "F").Value
End If
Next
Next
End With
'Save updated Payroll Template
wb2.Save
End Sub

Automatically delete row in sheet 2 if stock number is the same and if date entered in sheet1?

Hello:) I need excel to automatically (thus vba would be preferrable) delete a row in Sheet2 if column D (stock number) for that row in Sheet2 matches the G column for any row in sheet 1 and if column AY's value for that row in sheet1 is a date. does anyone know how to do this? please let me know if i was unclear i would be happy to clarify further.
thank you so much, elias
I would read the data up from sheet one and then look through the second sheet for matches. You will need to add a reference for the adodb recordset. In the VBA IDE on the tools pulldown menu select references. Select "Microsoft ActiveX Data Objects 2.8 Library".
Private Sub CommandButton8_Click()
Dim rs As New ADODB.Recordset
Dim ws As Excel.Worksheet
Dim lRow As Long
Set ws = ActiveWorkbook.Sheets("Sheet1")
ws.Activate
'Add fields to your recordset for storing data.
With rs
.Fields.Append "Row", adInteger
.Fields.Append "StockNumber", adChar, 50
'.Fields.Append "DateAY", adChar, 50
.Fields.Append "DateAY", adDate
.Open
End With
lRow = 1
'Loop through and record what is in the columns
Do While lRow <= ws.UsedRange.Rows.count
rs.AddNew
rs.Fields("Row").Value = lRow
rs.Fields("StockNumber").Value = ws.Range("G" & lRow).Value
rs.Fields("DateAY").Value = ws.Range("AY" & lRow).Value
rs.Update
lRow = lRow + 1
ws.Range("A" & lRow).Activate
Loop
If rs.EOF = False Then
rs.MoveFirst
End If
'Switch to the second worksheet
Set ws = Nothing
Set ws = ActiveWorkbook.Sheets("Sheet2")
ws.Activate
'Loop through the second sheet looking at the stock numbers.
lRow = 1
Do While lRow <= ws.UsedRange.Rows.count
rs.Filter = ""
rs.Filter = "StockNumber='" & ws.Range("D" & lrow).Value & "'"
if rs.RecordCount > 0 Then
'We have a match, now check if there is a date.
If trim(rs.Fields("DateAY")) <> "" then
'It has a date, delete the current row
ws.Rows(lRow).entireRow.Delete
lRow = lRow - 1
End if
End if
lRow = lRow + 1
ws.Range("A" & lRow).Activate
Loop
End sub
I'm not sure if the row needs incremented when you delete a row since the next row will now be your current row. You may need to add
lRow = lRow - 1
right after you delete the current row. That will bring it back a row and then the normal increment will effectively set it back to the current row.

Copy Rows from Sheet With Same Leading Cell Text

So I have been stumped by this for a while now. I have two sheets with some values in column 1 bolded. I wish to filter through the values on the second sheet (Mult. Year Set up) and copy the entire row of data with the same column 1 bolded Text.
Page from Which data is to be coppied
Page Which Data is to be coppied to
For example I would like to take the "Backhoes" row from the first link and copy it to the "Backhoes" row on the Second link.
The major issue here is that the rows on the first page with bolded text are variable and may not allways have the same row number. The row numbers will change when more equipment is added.
Thanks in advance for the assistance, as I said, I have been working on this for a few days now.
Additionally, the data must be transposed from a row to a column, but that is a minor detail.
Look at something like this.
1. Loop through the rows looking for bold cells.
2. Loop through all the columns and copy the data from the source sheet to the target sheet.
Dim lRow As Long
Dim lCol As Long
Dim lRowFind As Long
Dim sourceSheet As Excel.Worksheet
Dim targetSheet As Excel.Worksheet
Set sourceSheet = ActiveWorkbook.Sheets("Current Year Budget")
Set targetSheet = ActiveWorkbook.Sheets("Mult. Year Set up")
lRow = 5
Do While lRow <= sourceSheet.UsedRange.Rows.count
'You could do it by name
'If ws.Range("A" & lRow).Value = "Backhoes" Or ws.Range("A" & lRow).Value = "Graders" Then
'If we are looking for variable rows by them being bold. We can do this.
If sourceSheet.Range("A" & lRow).Font.Bold = True Then
'Find the row on the target sheet where we want to write the data.
lRowFind = 1
Do While lRowFind <= targetSheet.UsedRange.Rows.count
If targetSheet.Range("A" & lRowFind).Value = sourceSheet.Range("A" & lRow).Value Then
Exit Do
End If
lRowFind = lRowFind + 1
Loop
'Now we have a row that we want to copy.
For iCol = 1 To sourceSheet.UsedRange.Columns.count
targetSheet.Cells(lRowFind + iCol, 2).Value = sourceSheet.Cells(lRow, iCol).Value
Next iCol
End If
lRow = lRow + 1
sourceSheet.Range("A" & lRow).Activate
Loop

I need to create new sheets based on unique names found in column A. Current Code generates excess data in certain sheets

I have the following code so far based on questions asked by other people.
I have a set of names listed in column A, and 216 columns and 9725 rows of data.
Currently using the following code I get the new sheets created except along with the unique names and its relevant data I get many cells filled with "#N/A".
In certain cases, the name Bob for example will be populated in a new sheet called Bob but the first column will have Bob and all relevant data and once all Bobs rows are shown it is follower with many rows with #N/A and all columns with #N/A.
In other cases the sheet will be created for Charles and all of Charles data will be listed, then many rows of #N/A and then all of the master-data including other peoples names which I need to avoid.
I want each individual sheet to only have the info based on the name of the person on that sheet. All of the data gets copied as I verified the number of accurate cells that get populated yet I get these #N/A cells and duplicated extra data and I'm not sure how to stop it from being populated? Any help in cleaning the code would be appreciated!!
Code:
Sub CopyDataFromReportToIndividualSheets()
Dim ws As Worksheet
Set ws = Sheets("FormulaMSheet2")
Dim LastRow As Long
Dim MyRange As Range
Worksheets("FormulaMSheet2").Activate
LastRow = Range("A" & ws.Rows.Count).End(xlUp).Row
' stop processing if we don't have any data
If LastRow < 2 Then Exit Sub
Application.ScreenUpdating = False
' SortMasterList LastRow, ws
CopyDataToSheets LastRow, ws
ws.Select
Application.ScreenUpdating = True
End Sub
Sub SortMasterList(LastRow As Long, ws As Worksheet)
ws.Range("A2:BO" & LastRow).Sort Key1:=ws.Range("A1")
', Key2:=ws.Range("B1")
End Sub
Sub CopyDataToSheets(LastRow As Long, src As Worksheet)
Dim allAgentNameCells As Range
Dim cell As Range
Dim Series As String
Dim SeriesStart As Long
Dim SeriesLast As Long
Set allAgentNameCells = Range("A2:A" & LastRow)
SeriesStart = 2
Series = Range("A" & SeriesStart).Value
For Each cell In allAgentNameCells
If cell.Value <> " " And cell.Value <> "" Then
' Condition ` And cell.Value <> "" ` added for my testdata. If you don't need this, please remove.
' Current Row's Series not SPACE
If cell.Value <> Series Then
SeriesLast = cell.Row - 1
CopySeriesToNewSheet src, SeriesStart, SeriesLast, Series
Series = cell.Value
SeriesStart = cell.Row
End If
End If
Next
'' copy the last series
SeriesLast = LastRow
CopySeriesToNewSheet src, SeriesStart, SeriesLast, Series
End Sub
Sub CopySeriesToNewSheet(src As Worksheet, Start As Long, Last As Long, name As String)
Dim tgt As Worksheet
Dim MyRange As Range
If (SheetExists(name)) Then
MsgBox "Sheet " & name & " already exists. " _
& "Please delete or move existing sheets before" _
& " copying data from the Master List.", vbCritical, _
"Time Series Parser"
End
Else
If Series = " " Then
End
End If
End If
Worksheets("FormulaMSheet2").Activate
' Worksheets.Add(after:=Worksheets(Worksheets.Count)).name = name
Worksheets("FormulaMSheet2").Copy After:=Worksheets(Worksheets.Count)
ActiveSheet.name = name
Set tgt = Sheets(name)
' copy data from src to tgt
tgt.Range("A2:BO2" & Last - Start + 2).Value = src.Range("A" & Start & ":BO" & Last).Value
End Sub
Function SheetExists(name As String) As Boolean
Dim ws As Variant
For Each ws In ThisWorkbook.Sheets
If ws.name = name Then
SheetExists = True
Exit Function
End If
Next
SheetExists = False
End Function
You need replace the
tgt.Range("A2:BO2" & Last - Start + 2).Value = src.Range("A" & Start & ":BO" & Last).Value
to
src.Range("A" & Start & ":BO" & Last).SpecialCells(xlCellTypeVisible).Copy Destination:=tgt.Range("A2:BO2" & Last - Start + 2)
I found what I needed at the following site: http://www.rondebruin.nl/win/s3/win006_5.htm .
I figured if anyone else was looking for similar code it would help taking a look at the site.

Need to find the last row in a spreadsheet before copying and pasting data from Sheet 1 to Sheet 2

This site has helped me immensely with VBA for a while now, so thanks for that! But I just can't seem to get this code to work and I've look at so many examples. What's happening is that I'm archiving data on another sheet once the current date is 4 days ahead of the due date. Everything works like it should, but every time the macro executes, the data on sheet2 is erased and copied over. I need my code to find the last row on sheet2 and copy the data from sheet1 to sheet2 so all the data is there. Thanks!
Sub archive()
Dim LastRow As Long
Dim i As Long
LastRow = Range("M" & Rows.Count).End(xlUp).Row
For i = 3 To LastRow
If Worksheets("Sheet1").Range("M" & i) - Date <= -4 And Worksheets("Sheet1").Range("N" & i).Value = "DONE" Then
Sheet2.Select
Range("A" & i).EntireRow.Value = Sheet1.Range("M" & i).EntireRow.Value
Sheet1.Range("M" & i).EntireRow.Delete
End If
If Worksheets("Sheet1").Range("L" & i) = "" Then
Exit For
End If
Next i
End Sub
Here I've taken your code and changed it to use worksheet objects. I've not tested this on any data as you haven't provided any to use, but it gives you an idea of how to implement it.
Also, in your code you weren't finding the last row of Sheet2, you were putting the data in row i, which starts at 3.
You also need to watch out when you delete the row of data from sheet1, as this shifts the rest of the data up, so the next iteration of the loop may not find the next row of data/ skip a row of data.
Sub archive()
Dim LastRow As Long
Dim LastRowSht2 As Long
Dim i As Long
Dim sht1 As Worksheet
Dim sht2 As Worksheet
Dim rowCount As Long
Set sht1 = Worksheets("Sheet1")
Set sht2 = Worksheets("Sheet2")
LastRow = sht1.Range("M" & Rows.Count).End(xlUp).Row
rowCount = 3
For i = 3 To LastRow
If sht1.Range("M" & rowCount) - Date <= -4 And sht1.Range("N" & rowCount).Value = "DONE" Then
LastRowSht2 = sht2.Range("A" & Rows.Count).End(xlUp).Row + 1 '+1 so it doesn't overwrite the last row
sht2.Range("A" & LastRowSht2).EntireRow.Value = sht1.Range("M" & rowCount).EntireRow.Value
sht1.Range("M" & rowCount).EntireRow.Delete
Else
rowCount = rowCount + 1
End If
If sht1.Range("L" & rowCount) = "" Then
Exit For
End If
Next i
' clean up
set sht1 = nothing
set sht2 = nothing
End Sub