Sum() Across Dynamic Number of Sheets

2019-04-12 17:07发布

Hello all and thanks for your help ahead of time,

I have an excel sheet that is solely to take the summation of multiple sheets. Best and most simply put, the formula is something like =sum(Sheet1!A1,Sheet2!A1,Sheet3!A1,Sheet4!A1). There are a few issues that complicate matters though. First off all, I do not know the number or order of the sheets to sum, nor do I know their name. This formula will be copied into ~150 other cells, so I need the summation to be dynamic instead of physically adding sheets to ~150 cells every time. (Also the configuration and naming of the sheet does not allow for easy dragging for formulas.)

So first of all, I thought I could write it using an indirect() reference. I made a column to list all the sheets names that would be added into each cell. Unfortunately, concatenate() cannot be used over arrays, so I had to resort to a UDF seen below:

Function CONCAT(Delimiter As Variant, ParamArray CellRanges() As Variant) As String
Dim Cell As Range, Area As Variant
If IsMissing(Delimiter) Then Delimiter = ""
For Each Area In CellRanges
If TypeName(Area) = "Range" Then
For Each Cell In Area
If Len(Cell.Value) Then CONCAT = CONCAT & Delimiter & Cell.Value
Next
Else
CONCAT = CONCAT & Delimiter & Area
End If
Next
CONCAT = Mid(CONCAT, Len(Delimiter) + 1)
End Function

Using the UDF, I could get a large string with the proper syntax such as =CONCAT("'!A"&(B1+1)&",'",Array_of_Sheets)&"'!A"&(B1+1). The CONCAT() takes a separator as the first parameter and the array(s) as the second parameter. I then append the "separator" on the end of the string as well to output something such as Sheet1'!A1,'Sheet2'!A1. I thought at this point, a simple =sum(indirect(STRING)) would be sufficient, but =sum(indirect("Sheet1!A1,Sheet2!A1,Sheet3!A1,Sheet4!A1")) does not work since indirect() cannot seemingly process the commas.

So to solve this, I switched the =CONCAT() to =CONCAT("'!A"&(B1+1)&"+'",Array_of_Sheets)&"'!A"&(B1+1) to output Sheet1'!A1+'Sheet2'!A1. Now I wrote another UDF to force it to evaluate seen below:

Function EVAL(RefCell As String)
Application.Volatile
EVAL = Evaluate(RefCell)
End Function

This worked! Awesome, right? After some use and testing though, it seems to not be quite consistent. When we have other sheets open, it does not always evaluate (and we will have other sheets open). There is some other inconsistencies that I read about with using volatile in the UDF, but I cannot find them.

So for my ideal, I would like to do this without any VBA at all, but I suspect that won't be possible. I would rather not depend on the user to manually recalculate worksheets (partly the reason for volatile). Finally, I just want it to sum() in a consistent manner so if a user opens the document and just hits print, the user does not need to confirm the functions are evaluating correctly (check for #REF errors or manually add up the values to make sure they are correct). I am hoping to find an alternative to using the eval() equation.

EDIT for additional information below

I had experimented with using 3D arrays, but there was a few issues I ran into. Lets say the workbook has eight sheets. Of those eight sheets, only two may feed into this total sheet. There is a setup sheet, the total sheet, a data entry sheet, and a data analysis sheet. The total sheet totals off of the data analysis sheets. Typically a data entry sheet coincides with a data analysis sheet. So the user may make copies of both the data entry and the data analysis sheets. The most logical order of the sheets would begin with the setup sheet, then each pairing of the data entry and data analysis sheets, and the total sheet. Obviously, this setup will not work with a 3D array. We would have to reorder the sheets and make sure the user knows that the order of the sheets actually matters (an atypical configuration). There will also be instances where one or two data analysis sheets will not be included in the total sheet. So we may end up with a sheet configuration of setup \ data entry (1) \ data entry (2) \ START \ data anaylsis (1) \ data analysis (2) \ END \ totals \ data entry (3) \ data entry (4) \ data anaylsis (3) \ data analysis (4). My feeling was that a 3D array configuration would almost make the addition too dynamic. I would rather have an explicit list then depend on the user being confident in moving the sheets around to have the proper totals.

2条回答
兄弟一词,经得起流年.
2楼-- · 2019-04-12 17:50

Good news! You can make your life much simpler - by using a 3D formula:

If you use this formula: =SUM(Sheet1:Sheet3!$A$1:$B$2), all cells in A1:B2 will be summed - across all sheet from Sheet1 to Sheet3!

Quite often, it is helpful to use two "helper" sheets to handle an open/changing number of worksheets:

  1. Insert two worksheet called START and END
  2. Span your 3D formula across those two sheets
  3. Move all the sheets between or move START and END sheet around those sheets (of course, this can be done in step 1, too)
  4. Hide the two helper sheets

Check out this link for further instructions.

查看更多
何必那么认真
3楼-- · 2019-04-12 18:06

Here's another approach:

Assuming that your user's data entry sheets are labelled according to some kind of system that has to be strictly adhered to (e.g. "Data entry 1", "Data entry 2", etc.), you could build a summary table using INDIRECT - and then total this table instead.

In detail, you'd have a helper table in your 'Totals' sheet, looking something like this:

   Col A         Col B    Col C    Col D
1                         C5       X7
2  Sheet name    Exists?  Value 1  Value 2
3  Data entry 1  TRUE          10       20
4  Data entry 2  FALSE          0        0
5  Data entry 3  TRUE          20       30
6  ...
.
.
.
20 More sheet names than you'll ever get just to be sure!
  • In cells C1 and the following columns you'd write the address of the cell you want to total. If you have a template and want to keep the references dynamic (usually the static nature of the address is a source of bugs here), use =ADDRESS(ROW(Template!C5);COLUMN(Template!C5)) to get the dynamic address.
  • In column B you'd use this formula: =NOT(ISERROR(INDIRECT(ADDRESS(1,1,,,A3))))
  • In column C and the following columns, use this: =IF(B3,INDIRECT("'"&A3&"'!"&C$1),0)

Now you only need to total the columns C and so forth.

查看更多
登录 后发表回答