This question already has answers here:
Excel VBA Sum from Multiple Sheets
(3 answers)
Closed 3 years ago.
I need to sum values across multiple sheets. The sheets will always have different names as they are set by the date.
I have a summary sheet as the first sheet in my workbook.
From a field in my summary sheet, I'd like to read through each sheet and if the value of the fields match it adds the values together.
For example, pseudo code would be, Go through each worksheet and if SummarySheet.Range("A24").value is in range(G1:G200) sum up the corresponding cell in range("H1:H200").
I've tried sum if and sum product.
I've tried the below code. Which bombed!
For Each Cell In Range("A24:A224")
For Each ws In ThisWorkbook.Worksheets
For Each i In ThisWorkbook.ActiveSheet.Range("G2:H200")
If Cell.Value = i.Value Then
ActiveCell.Offset(0, 1).Select
Cell.Value = Cell.Value + i.Value
End If
Next i
Next ws
Next Cell
Your code is referencing "ActiveSheet" and "ActiveCell" but it's not activating anything within the loop.
It is very difficult to guess what your code is trying to do. This answer is more about good coding practice than specific recommendations. My hope is this will allow you to correct your own code.
ActiveCell.Offset(0, 1).Select serves no purpose. The ActiveCell is the cell containing the cursor. You do not position the cursor at the beginning of the routine to a first cell and you neither read from nor write to the ActiveCell during the rest of the macro.
For Each ws In ThisWorkbook.Worksheets serves no purpose since you do not use ws. In For Each i In ThisWorkbook.ActiveSheet.Range("G2:H200") did you mean to write For Each i In ws.Range("G2:H200")? I cannot see anywhere else you might have meant to use ws.
Each Cell In Range("A24:A224") references the ActiveSheet. This is whatever worksheet happens to be active when the macro was started. My guess is that Range("A24:A224") is in the summary worksheet. If the user happens to be looking at another worksheet when they start the macro, that other worksheet is the ActiveSheet and not the summary worksheet. I assume you want to accumulate totals in the summary worksheet. If the wrong worksheet is active when the macro is started, that worksheet will be corrupted beyond recovery. UnDo cannot undo what a macro has done so you will have to revert to the previous copy of the workbook. I hope you create a new copy of the workbook before trying experimental macros so you can revert to an undamaged version. You need something like:
Dim WshtSumm As Worksheet
Set WshtSumm = Worksheets("Summary")
For Each Cell In WshtSumm.Range("A24:A224")
: : : : : : :
Next
In my opinion, you should not use names like Cell because it is too similar to the reserved word Cells. You should not use names like i because such names are meaningless. Perhaps it does not matter in such a small macro but, as your macros get bigger, meaningful names will be a real help when you or someone else looks at this macro in six or twelve months. Few macros last unchanged for ever; they are updated every few months to address changing requirements. I do not know what Cell and i represent but perhaps CellSumm and CellData would be better names.
Consider ThisWorkbook.Worksheets and Range("A24:A224"). I have just told you that you should have identified which worksheet holds Range("A24:A224"). This was because your workbook holds several worksheets any of which could be active. If you have a workbook that has only one worksheet, Range("A24:A224") might be OK. Personally, I avoid unqualified ranges because it is so easy to add a new worksheet. However, is it likely that you will have two workbooks or more workbooks open? Yes, it is possible to run a macro in one workbook from another workbook but is this likely? ThisWorkbook identifies the workbook holding the macro. If you are concerned that the wrong workbook will be active, you are absolutely correct to specify which collection of worksheets you wish to access because each workbook has its own collection. However, Worksheets on its own will normally be sufficient. This is something you need to think about, particularly as your macros get more complicated: do I need to qualify this range or worksheet or anything of which you might have more than one?
Consider:
For Each ws In ThisWorkbook.Worksheets
For Each i In ws.Range("G2:H200")
I have not qualified ws because a worksheet has a property Parent which identifies its workbook. You qualified ws when you created it.
One of the worksheets in ThisWorkbook.Worksheets will be the summary worksheet. Do you want to examine cells in the summary worksheet? I assume not. You need something like:
Dim WshtSumm As Worksheet
Dim WshtOther As Worksheet
Set WshtSumm = Worksheets("Summary")
For Each WshtOther In Worksheets
If WshtOther.Name <> WshtSumm.Name Then
: : : : : : :
End If
Next
This code only examines worksheets whose name does not match the summary worksheet's name. Whenever you look through an entire collection of something, you need to ask yourself: do I really want to look at everyone?
Consider:
If Cell.Value = i.Value Then
ActiveCell.Offset(0, 1).Select
Cell.Value = Cell.Value + i.Value
End If
Where you have written ActiveCell, I suspect you meant Cell giving:
If Cell.Value = i.Value Then
Cell.Offset(0, 1).Select
Cell.Value = Cell.Value + i.Value
End If
This would make a little more sense but not much. You search Range("G2:H200") of every worksheet for a value that matches a value in Range("A24:A224") of the summary worksheet. If you find that matching value you add it to the next cell in Range("A24:A224"). You tell us nothing about the summary worksheet or the data worksheets so this may be sensible but it does not feel sensible. If you find a match on "A30", you add the value of "A30" to "A31". But then you search for a match on the amended "A31". I cannot imagine any requirement for which this would be sensible.
Even if I thought this was sensible, I don’t think it will work. If I write:
For X = 1 to 10
: :
Next
I cannot change the value of X within the For Loop. I have not tried recently but my recollection is that the attempt to change X is ignored. You are trying to change the equivalent of X for a For Each statement. I have not tried but I suspect your attempt to change Cell will fail. Even if it can change Cell you should not. Select is a slow command and you should only use it if it is essential. Try something like:
If Cell.Value = i.Value Then
Cell.Offset(0, 1).Value = Cell.Offset(0, 1).Value + i.Value
End If
I hope the above helps. If it does not, please provide details of the two ranges so I can better understand what you are trying to do.
I just tried this and it worked perfect.
Sub GenerateTheFormula()
Dim x, Formula
Formula = "=SUM(" 'Formula begins with =SUM(
For x = 3 To Sheets.Count
Formula = Formula & Sheets(x).Name & "!A1," 'Add SheetName and Cell and Comma
Next x
Formula = Left(Formula, Len(Formula) - 1) & ")" 'Remove trailing comma and add parenthesis
Range("B1").Formula = Formula 'Where do you want to put this formula?
End Sub
Idea from here:
Excel VBA Sum from Multiple Sheets
Related
Looking for a solution to probably a very simple problem but somehow I can not seem to find an answer.
I am looking for a way to copy the first cell of each column. I want to use the copied value to filter it in another workbook. I want to paste the filtered cells below and then repeat the process until all columns have been gone through.
Thus what kind of loop would I use to copy every first cell at a time, store it, then do something with it and then copy the next first cell of a column for all columns?
Could someone please me with this? Your help is much appreciated.
For i = 1 To 207
Columns(i).Select
ActiveCell.Offset(0, 0).Range("A1").Select
Selection.Copy
'How do I store the copied value to use it in an autofilter?
Next i
End Sub
Instead of selecting – and you should avoid using select –
You can set the value to a variable varname = Cells(1,i) for later use,
or better yet, apply directly to your filter.
Dim i As Long
Dim wb2 As Workbook
Set wb2 = Workbooks("Book2") 'the "Other" workbook name here
For i = 1 To wb2.Worksheets.Count 'looping each sheet
wb2.Worksheets(i).Range("A1").AutoFilter Field:=1, Criteria1:= _
ThisWorkbook.Sheets(1).Cells(1, i)
Next i
This is just an example which makes a lot of assumptions that more than likely does not fit with your reality, these has to be changed to fit your scenario, ofc.
But it would go through the specified amount of worksheets in the specified workbook, and filter the first column with the value obtained from the first workbook.
Then, there are quite a few questions on how to copy a filtered range that might give a hint.
I have a unit matrix that illustrates work items for an apartment complex. It expands to 5 floors and has more work items than just the kitchen scope. My end goal is to have a sheet for each unit, listing the specific items needed for that unit. It would be very helpful once construction begins.
I want to do 2 things. 1 - Create new sheets for each unit (C5:C124) using the template. 2 - Copy over the information based on what is marked with an "X"
I know how to create a macros that will create blank sheets from the number of units I have. I'm stuck on integrating the template.
Thank you for reading.
Unit Scope Matrix
Template
Edit 1:
Here is my new code that can take a range of room#s and create new sheets from it. Now I would like to copy and paste the the row next the the according room# cell and paste in the appropriate sheet.
Sub CreateSheets()
Dim rng As Range
Dim cell As Range
On Error GoTo Errorhandling
'Creates popup box asking for the room numbers
Set rng = Application.InputBox(Prompt:="Select cell range:", _
Title:="Create sheets", _
Default:=Selection.Address, Type:=8)
For Each cell In rng
'Check if cell is not empty
If cell <> "" Then
'Insert worksheet and name the worksheet based on cell value
Sheets("Template").Copy After:=Sheets("Unit Types")
'Name new sheet based off two cells on Bid Summary List Cells (Bi and Di)
ActiveSheet.Name = "UNIT-" & cell
'This is where I think I should add the copy/paste lines... but I don't know how.
'Copy unit# row and paste in correct worksheet
'Range("XX:XX").Copy Range("XX:XX")
End If
'Continue with next cell in cell range
Next cell
'Go here if an error occurs
Errorhandling:
'Stop macro
End Sub
The code to copy the template and rename it is easy enough to make. Start recording, do one manually, stop recording, then press Alt-F11 to see how it's done, then steal from that to make your own function.
I suspect you'll end up with something that looks like
Function NewSheet(nm as String) as Worksheet
Dim template As Worksheet
set template = ActiveWorkbook.sheets('template')
set NewSheet = template.copy(ActiveWorkbook)
NewSheet.name = nm // change the tab name
NewSheet range('B1') = nm // add to the sheet as well
End Function
(warning: syntax and method names might unintentionally be wrong, this is intended to give you a head start, not do it for you)
and then you'll need to write a macro that loops through column C and calls your function. In this case you can ignore that NewSheet returns a new Worksheet object, but this way provides flexible code for future needs. Also, by isolating what you need to do to make a new worksheet as its own function that's called multiple times, It's easier to reason through and test, and the looping code that calls it is much easier to read as well.
Try searching for "Excel VBA looping examples" to get a head start on looping if you are unfamiliar.
How do you force an excel workbook to use itself as a source for worksheet links?
I'm writing a VBA macro to automate the process of adding an excel worksheet into a workbook. The worksheet (sheet1) takes only certain (but very many) responses from within the several sheets (response1, response2, response3) of the questionnaire. As a result of this, sheet1 contains lots of cell references that don't lead anywhere until after the macro is run.
For instance a1 in sheet1 "='response1'!b6". This returns a #REF! error before the macro is run (which is fine).
After the macro is run sheet1 is now inside the correct workbook, and "='response1'!b6" is now a valid cell reference.
Except excel doesn't realise this until after I manually click the cell in Sheet1, press f2, then press enter. When I do this the cell is correctly populated. The trouble is there are large numbers of cells.
Is it possible to construct a VBA macro that will simulate this process of selecting formula boxes and pressing "Enter". Looking up people with similar problems, most have had the problem remedied by some combination of f9, turning automatic calculation back on, or ActiveSheet.Calculate or a variant. None of these have worked, it appears to be an issue with references, even though the references point to valid locations.
Otherwise, is it possible to use VBA to perform the same process as:
Data > Edit Links > Update Values
But in this case we'd need to specify the currently opened workbook as it's own source. Is there any way to do this?
When I manually selected the current workbook as the source under "Edit Links > Update Values" excel strangely repeats the worksheet name in the cell references, like this: "='[response1]response1!B31", which then fails to update when cell b31 changes, so this is not a solution.
Here's the code that runs on button press:
Private Sub CommandButton1_Click()
'copy worksheet into responses
Dim CopyFromWbk As Workbook
Dim CopyToWbk As Workbook
Dim CopyToWbk As Workbook
Set CopyFromWbk = Workbooks("Addition.xlsm")
Set ShToCopy = CopyFromWbk.Worksheets("Sheet1")
Set CopyToWbk = Workbooks("QuestionnaireResponses.xlsm")
ShToCopy.Copy After:=CopyToWbk.Sheets(CopyToWbk.Sheets.Count)
Workbooks("QuestionnaireResponses.xlsm").Activate
'Put code to update links in here
ThisWorkbook.UpdateLink Name:="myfilepathgoeshere.QuestionnaireResponses.xlsm", Type:=xlExcelLinks
'End update links
Thanks for any help, this one's a head scratcher.
Great idea from #Kyle. For those who having trouble forcing cell references to update, TextToColumns works.
However TextToColumns draws an error if the source range is empty, so if there's any chance of that being the case use an if statement with no action attached to skip over those instances.
My successful code looks like this:
Dim i As Integer
For i = 1 To 1004
'Scans through row 2 from col A onwards
'If cell is empty, does nothing.
'If cell is not empty, performs TextToColumns where source range = target range.
If IsEmpty(Workbooks("QuestionnaireResponses.xlsm").Worksheets_
("response1").Cells(2, i)) Then 'Does nothing if the cell is empty.
Else
Workbooks("QuestionnaireResponses.xlsm").Worksheets("response1").Cells(2, i).Select
Selection.TextToColumns Cells(2, i) 'Performs TextToColumns
End If
Next
All of my data is on the same long row. To apply the above to an entire spreadsheet, just nest everything between, and including, For i = 1 and Next within another For loop with different letter replacing i.
'First-timer here, so thanks for your patience!
I have a workbook with multiple sheets, and I need to:
1)Select pairs of worksheets (index and index +1)
2)Copy them into a new workbook
3)Rename the sheets with whatever label is in a referenced cell (such as A1)
4)Then repeat for every pair of sheets in the workbook (dozens of pairs)
Since I am very new to vba (only took one class), I am easily confused. The only part I have so far is referencing cell A1 to rename the sheet to whatever text string is in that cell. I can't figure out the rest of it. I'm thinking it has something to do with worksheet.index or worksheets(index) and some kind of loop where index adds 2 and activates that sheet and the next one before copying: sheets 1 and 2, then sheets 3 and 4, etc.
Private Sub Worksheet_Calculate()
Dim s As String
s = "sheet 1"
If Range("A1").Value <> "" Then
s = Range("A1").Value
End If
ActiveSheet.Name = s
End Sub
The way I understand it, cell A1 has to be checked to be sure it's not null because you can't name a sheet with a null value. The usage and syntax on the rest of it is quite beyond me at this point, but I really want to learn and eventually become a contributing member of this community. I'm brand new at my job and only working part-time, so bringing a solution to my boss might mean me becoming permanent. Therefore, a BIG, BIG THANKS!!!
Option Explicit
Private Sub Worksheet_Calculate()
Dim s As String
Dim ws As Worksheet
Dim wsi As Worksheet
Set wsi = Sheets("Sheet4")' You need to create a name for the sheet to paste into
For Each ws In ActiveWorkbook.Worksheets
If Not IsEmpty(ws.Range("A1")) and ws.name = "INDEXCOPY" Then
ws.Name = ws.Range("A1").Value
ws.Range("A1:" & ws.Range("A1").SpecialCells(xlCellTypeLastCell).Address).Copy _
Destination:=wsi.Range("A1")
End If
Next ws
End Sub
This code will get you started, however, you need a way to distinguish between an index sheet you want to copy from, versus an index sheet you want to copy to. You would need to add an additional if then statement, see comment above. I would look here - http://www.rondebruin.nl/win/s3/win006.htm
I want to know how exactly to incorporate a VLOOKUP function into my Excel worksheet via VBA; I am completely comfortable with the VLOOKUP function when entering it directly into a cell, but I am completely new to VBA.
A little info:
I have ~5500 rows of data;
I have a number of named ranges (such as catNo, catNoRange, to name but two);
I want to use VBA to check catNo against catNoRange and return the value of the sixth (6th) column in catNoRange;
I also want to know how - and where - to display the result once I have it; ideally I would like it to appear in cell J4 (and the corresponding cells down to J5500).
How can I achieve this?
Additional Info:
For those of you wondering why I don't just use a regular VLOOKUP to achieve this: I want to use VBA because I have written a script which checks to see if certain cells are empty, and if at least one of them is populated, then the VLOOKUP will execute, taking the value of the populated cell for the main search critera.
Code Update:
Following some advice, I have opted to attempt to use the Find function instead of VLOOKUP. It doesn't work; here is what I have:
Sub findCode()
Result = WorksheetFunction.Find("ABI0010P", "5012616173004", "33787")
Range("J4").Value
End Sub
For the find function you want to have something like this
Dim ws As Worksheet
Set ws = Worksheets(2)
Rowz = ws.Cells(rows.Count, 1).End(xlUp).Row
k = 2
searchvalue= ws.Cells(k, 2)
Set SEARCHRANGE= ws.Range("A2:A" & Rowz)
Set Findx = SEARCHRANGE.find(searchvalue, LookIn:=xlValues)
If Not Findx Is Nothing Then ws.Cells(k, 4) = "what ever you want"