01 / 14Nine Tabs, One Stack, £12,565.25 Short

VSTACKExcelDynamic Arrays

VSTACK in Excel: Combine Data From Multiple Sheets

The Group Sales Report Read £63,208.10 for a Month Worth £75,773.35, Because One Branch Stacked Its Unit Counts Where the Money Goes and Another Was Never in the Stack at All

16 Oct 202613 min read

UsesFILTERSUMIFSCOUNT, COUNTASUMPRODUCT

Brantwood Garden Centres reported £63,208.10 of October sales. The shops took £75,773.35. Every branch tab balanced to its own till export, nothing errored, and the group tab came out £12,565.25 light.

That tab is one formula. =VSTACK(Alderwell:Thurlby!A2:F200) stacks nine branch sheets into a single list, and SUMIFS reads the list. It is the tidiest thing in the workbook.

A stack aligns by position, never by heading. Column F of one tab lands under column F of the next, whatever the two headings say, and nothing in Excel compares them.

MeasureFigure
Branch tabs9
Sales in those tabs1,072
Rows in the stack1,592
Reported£63,208.10
Actually taken£75,773.35
Short by£12,565.25

Four faults made that gap, and two of them pull in opposite directions, so the total looked like a quiet month. A branch missing, a tab counted twice, two columns swapped, and a range that stops at row 200.

What you need. VSTACK and HSTACK need Microsoft 365 or Excel 2024. FILTER, SORT, UNIQUE, LET and spill references such as =H2# need Microsoft 365 or Excel 2021 and later.

SUMIFS, COUNTIFS, SUMPRODUCT, INDEX, COUNTA, MOD and 3-D references work in every version this century. Section 11 is for the build that has none of the new ones.

Garden centres are only the example. Depots, clinics, sites, funds, subsidiaries: one tab each, one summary tab, and the same silence.

01Nine Tabs, One Stack, £12,565.25 Short

Nine Branch Tabs, One Stack, £12,565.25 Short

The October reconciliation for Brantwood Garden Centres, in A2:I10. It is not the stack itself; it is the check somebody should have run against it. Column C is the sales each branch tab holds, D is the rows the stack took from that tab, F is the amount the stack carries and G is the amount the branch actually took. Six branches agree to the penny. Ferncliffe contributed nothing, because its tab sits outside the `Alderwell:Thurlby` span; Greenhall (2) contributed £9,110.00 that does not exist, because a copied tab landed inside it; Oakmere's £12,742.60 went into the units column and its 486 units into the money column, because the branch changed till systems and a stack aligns by position; and Thurlby lost £1,938.45 off the bottom, because A2:F200 is 199 rows and the branch wrote 214 sales. Net, the group reported £63,208.10 against £75,773.35 taken. Gross, £30,785.25 of October landed somewhere other than where it belongs.

ABCDEFGHI
1
Branch
Tab
Sales in the tab
Rows stacked
Units stacked
Amount stacked
Amount in the tab
Difference
What happened
2
Alderwell
Tab 1
148
199
1284
11284.4
11284.4
0
The template tab, unchanged since 2019: Units in E, Amount in F, 148 sales and 51 empty rows that the span picks up as zeros. Right, and the reason nobody looked at the others.
3
Bexmoor
Tab 2
121
199
967
8957.25
8957.25
0
Also right. Six of the nine branches agree to the penny, which is what a reconciliation is for: the three that do not are invisible until something counts them.
4
Ferncliffe
Tab 9
92
0
0
0
7480.2
-7480.2
Opened in September. Excel adds a new sheet where you ask for it and the manager asked at the end, one tab to the right of Thurlby. The span stops at Thurlby, so the branch is not in the report at all.
5
Greenhall
Tab 3
134
199
1102
9110
9110
0
The original Greenhall tab, correct in itself. Its figures are in the group total twice, which is not this tab's fault and not visible from this row.
6
Greenhall (2)
Tab 4
134
199
1102
9110
0
9110
A copy made with Move or Copy to try a different layout. Excel drops a copy immediately after the original, which is inside the span, so the stack took all 134 sales a second time.
7
Kirkmoor
Tab 5
96
199
812
6642.8
6642.8
0
The smallest branch, and the clearest view of the blank rows: 96 sales and 103 empty rows, all of which arrive in the stack as rows of zeros with 00/01/1900 in the date column.
8
Oakmere
Tab 6
157
199
12742.6
486
12742.6
-12256.6
New till system in September. Its export writes Amount in column E and Units in column F; every other branch does the opposite. The stack aligned the columns by position and filed the money as a count.
9
Stanbury
Tab 7
110
199
884
7215.5
7215.5
0
Right, and worth keeping in view. Most of this sheet is right, which is exactly why a group total that is £12,565.25 light reads as a quiet month rather than as four separate faults.

fxCells with formulas are highlighted in green

Hover over formula cells to see the formula and highlight referenced cells

The sheet above is not the stack. It is the reconciliation nobody had run against it: one row per branch tab, what the stack took from that tab, and what the tab itself totals.

Column F is the amount the stack carries. Column G is the amount the branch took. Six branches agree to the penny. Three do not, and one of the three is wrong in the group's favour.

BranchIn the stackIn the tabDifference
Ferncliffe£0.00£7,480.20−£7,480.20
Greenhall (2)£9,110.00£0.00+£9,110.00
Oakmere£486.00£12,742.60−£12,256.60
Thurlby£10,402.15£12,340.60−£1,938.45

Net, the group is £12,565.25 short. Add the four faults without their signs and £30,785.25 of October landed somewhere other than where it belongs.

Scenario: Build this reconciliation for your own summary before you trust it. Put the branch names down a column, =SUMIFS(F:F,A:A,$A2) beside each one against the stack, and subtract each branch tab's own total. Every row should come out at nought.

Try it in the grid

02VSTACK Stacks by Position, Not by Heading

VSTACK takes arrays and puts each one under the last. Hand it a 3-D reference and it takes the same block from every tab between the two you name, in the order the tabs sit in.

Nothing in it reads a heading. The branch tabs came from one template in 2019, so for six years column F held the money on every one of them. That is a convention, and a convention is not a rule.

One cell says whether two tabs still agree:

=SUMPRODUCT(--(Alderwell!$A$1:$F$1=Oakmere!A1:F1))

It counts the headings that match, position by position. Six means aligned. Anything less is a tab the stack will scramble without a word, and on this workbook it returned 4.

Scenario: Write that SUMPRODUCT once for each tab you stack, pointing the second reference at a different sheet each time, and fill it down. Any row below your column count is a sheet to open before the next report goes out.

Try it in the grid

03The Branch That Stacked Counts as Money

Oakmere changed till systems in September. The new export writes Amount in column E and Units in column F. Every other branch, and the template they all came from, does it the other way round.

So the stack filed Oakmere's £12,742.60 of sales as a count of units, and its 486 units as £486.00. The group total lost £12,256.60 and gained nothing that looked like an error.

The tell was sitting in a column nobody totals. October's unit count came out at 20,324.60. A count of bags of compost with pence in it is a column that has been swapped, and it is the cheapest check on this page.

=SUMPRODUCT(--(MOD(E2:E1593,1)<>0))

That counts the rows whose unit figure is not a whole number. It came back with 149: the Oakmere rows whose amount carries pence. The other eight of its 157 sales were round pounds.

Scenario: Find the column on your own stack that can only hold whole numbers — units, headcount, tickets, cases — and type =SUMPRODUCT(--(MOD(E2:E1593,1)<>0)) over it. Anything but nought means some tab is feeding a different column into it.

Try it in the grid

04Every Tab Gives 199 Rows, Sales or Not

A 3-D reference takes the same block from every tab, so A2:F200 is 199 rows whether a branch sold 199 things or 96. The empty rows come through, and they do not come through empty.

A reference to an empty cell returns 0, not a blank. The stack holds 493 rows of zeros, with 00/01/1900 in the date column, and COUNTA counts every one of them as data.

That is why the average basket moved. £63,208.10 over 1,592 rows is £39.70. Over the 1,099 rows that are sales it is £57.51. Both are arithmetic; only one answers the question the report asked.

FILTER strips the padding, and the test is where people lose an afternoon: the blanks are zeros, so <>"" keeps every one of them.

=LET(s, VSTACK(Alderwell:Thurlby!A2:F200),
     FILTER(s, INDEX(s,0,1)<>0))

Scenario: Count what your stack is made of before you report an average from it. Put =ROWS(H2#) in one cell and =SUMPRODUCT(--(INDEX(H2#,0,1)<>0)) beside it; if the two disagree, every per-row figure in the report is diluted.

Try it in the grid

05The Branch That Was Never in the Stack

Ferncliffe opened in September. Its tab went where Excel puts a new sheet when you ask at the end: one place to the right of Thurlby. The formula says Alderwell:Thurlby.

A 3-D reference means the tabs physically between those two, in tab order. Not the branches, not the alphabet, and not whatever the workbook contains today. £7,480.20 sat one tab outside the span in a sheet that balanced perfectly on its own.

Bookend the span with two empty sheets. Name them Start and End, put one at each end of the branch tabs, and write =VSTACK(Start:End!A2:F200). Any tab dragged between them joins the stack.

That is the whole trick, and its price is the one in section 6: a span takes every tab inside it, including the ones you did not mean.

Scenario: Add the two bookend sheets to your own workbook and repoint the stack at them. Then drag an empty tab in between and watch =ROWS(H2#) grow the moment you let go of the mouse.

Try it in the grid

06The Copy That Was Counted Twice

Greenhall's manager wanted to try a different layout, right-clicked the tab and chose Move or Copy. Excel puts the copy immediately after the original, which is inside the span, so the stack took it.

£9,110.00 counted twice. On its own that would have shown: the group total would have been £9,110.00 too high and somebody would have asked. Against Ferncliffe and Oakmere it closed the gap and made four faults look like a slow October.

Two one-cell checks find it. =ROWS(s)-ROWS(UNIQUE(s)) returns 134, which is the number of rows the stack holds twice over. =COUNTA(UNIQUE(INDEX(s,0,1))) returns 8: seven branch names and the zero the blank rows contribute, for eight tabs stacked.

Scenario: Run =ROWS(H2#)-ROWS(UNIQUE(H2#)) over your own stack today. Anything above nought is a duplicated tab or a genuinely repeated line, and the difference between those two is worth five minutes.

Try it in the grid

07The Range That Stops at Row 200

Thurlby is the biggest branch: 214 sales in October. A2:F200 takes 199 rows from every tab, so fifteen of them, £1,938.45, are in the workbook and not in the report.

A span cannot grow its rows. It has no idea how long each tab is, and stretching it to A2:F5000 multiplies the blank rows by twenty-five without ever being certain.

Tables grow by themselves, and a 3-D reference cannot hold one. =VSTACK(tAlderwell,tBexmoor,tFerncliffe) over the branch tables follows each one as it fills, and names every tab you meant to include. A tenth branch then has to be added by hand.

Pick which half you want automatic: new tabs for free, or new rows for free. Then write a check against the half you did not pick, because that is the half that will go wrong.

ApproachNew tab joinsNew rows join
3-D span Start:End!A2:F200YesUp to row 200
Listed tables tAlderwell,tBexmoorNoYes
Power Query from a folderYesYes

Scenario: For each tab, put =COUNTA(Thurlby!A2:A10000) beside the stack's own =COUNTIF(INDEX(H2#,0,1),"Thurlby") and compare the pair. A branch whose stacked count stops exactly at your range length has lost its tail.

Try it in the grid

08Different Widths Pad the Stack With #N/A

List the arrays by hand instead of using a span and the widths stop being guaranteed. VSTACK pads the narrow ones with #N/A, so a five-column tab among six-column tabs gets a column of errors rather than a shift.

That is the kind behaviour. The errors are loud, they survive into every total that does not ignore them, and =IFERROR(VSTACK(...),"") turns them into blanks once you have understood why they are there.

HSTACK does the same thing sideways, padding short arrays with #N/A along the bottom. Either way the padding tells you the inputs disagree, which is more than the swapped column in section 3 ever did.

Scenario: Take a copy of one branch tab, delete a column from it, stack it and watch where the #N/A lands. Then put the column back, swap two columns instead, and watch the stack say nothing at all.

Try it in the grid

09Five Checks Above the Table

Each of these is one cell, and each one failed on this workbook. Put them above the stack where the report is read, not on a tab called Checks that nobody opens.

CheckFormulaHere
Rows in the stack=ROWS(H2#)1,592
Rows that are sales=SUMPRODUCT(--(INDEX(H2#,0,1)<>0))1,099
Rows held twice=ROWS(H2#)-ROWS(UNIQUE(H2#))134
Distinct values in column A=COUNTA(UNIQUE(INDEX(H2#,0,1)))8
Unit figures with pence=SUMPRODUCT(--(MOD(INDEX(H2#,0,5),1)<>0))149

The sixth costs a column rather than a cell, and it is the one that would have caught all four faults: the per-branch reconciliation from section 1, stacked total against branch total, every month.

10Reading the Stack: SUMIFS Needs a Range

A stack held inside LET is an array in memory, and SUMIFS and COUNTIFS do not take arrays. They take ranges. Point them at one and the answer is #VALUE!, which at least is an answer.

So let the stack spill onto a sheet. If =VSTACK(...) sits in H2, then H2# is a real range that grows with it, and =SUMIFS(M2:M1593,H2:H1593,$A2) totals a branch the ordinary way.

Inside LET, use SUMPRODUCT instead: =SUMPRODUCT((INDEX(s,0,1)=$A2)*INDEX(s,0,6)) reads arrays happily and gives the same number. XLOOKUP and FILTER take arrays too.

Scenario: Decide which of the two your workbook is. If other formulas point at the stack, spill it once and let them use H2#; if it is one total, keep it in LET and write the total with SUMPRODUCT.

Try it in the grid

11When Your Build Has None of This

Excel 2019 and earlier have no VSTACK, and Power Query's Append is older than all of it: Data ▸ Get Data ▸ Combine Queries ▸ Append, one query per tab, one output table.

Append matches on column names. Oakmere's swapped columns would have landed under the right headings, and a renamed column would have produced a new column of nulls instead of a silent mix.

It also reads each tab to its real last row, so there is no blank padding and no lost tail. What it does not do is recalculate: somebody has to press Refresh, and a stale refresh hides exactly the kind of gap this article is about.

Which to reach for. A stack is live, reads in one formula and needs Microsoft 365. Power Query is a refresh step, matches on headings and runs on any build since 2016.

If the tabs are really separate files in a folder, neither answer is the right one: point Power Query at the folder instead, and the thirteenth file joins without anybody editing a formula.

12Ten Traps

  1. A stack aligns by position. Two tabs with the same headings in a different order stack into nonsense with no error.
  2. A 3-D reference is tab order, not alphabet. Dragging a tab changes what the formula covers.
  3. A tab added at the end is outside the span. It joins nothing and says nothing.
  4. Move or Copy lands inside the span. The copy is counted in full.
  5. Blank cells arrive as 0. <>"" does not remove them; <>0 does.
  6. A blank date is 00/01/1900. Sort by it and the padding floats to the top.
  7. The row bound is fixed. The tab that outgrows it loses its tail quietly.
  8. Different widths pad with #N/A. Loud, and better than the alternative.
  9. SUMIFS refuses an array. Spill the stack, or total it with SUMPRODUCT.
  10. The stack is live. Delete a branch tab and the whole report changes while you watch.

13Mini Exercises

  1. Break it on purpose. Copy a branch tab with Move or Copy, leave it where Excel put it, and watch the group total rise by exactly that branch.
  2. Count the padding. Write =ROWS(H2#) and =SUMPRODUCT(--(INDEX(H2#,0,1)<>0)) over the example stack, and work out the average basket both ways.
  3. Catch the swap. Point =SUMPRODUCT(--(Alderwell!$A$1:$F$1=Oakmere!A1:F1)) at each tab in turn and find the one that returns 4.
  4. Fit the bookends. Add Start and End sheets, repoint the stack at Start:End!A2:F200, and drag Ferncliffe inside. Confirm the total rises by £7,480.20.
  5. Reconcile the lot. Build the column from section 1 and account for all £30,785.25, not just the £12,565.25 the group total admits to.

What to Take Away

Nothing on this workbook was broken. Nine branch tabs balanced, one formula stacked them, SUMIFS added the stack up, and every number in the report was the honest output of the formula above it.

What nobody had written down is that a stack is an assumption: that every tab has the same columns in the same order, that the tabs inside the span are the tabs you mean, and that the rows you read reach the bottom of the longest one.

Those three assumptions are each one cell to test. =SUMPRODUCT(--(Alderwell!$A$1:$F$1=Oakmere!A1:F1)) tests the first, =ROWS(H2#)-ROWS(UNIQUE(H2#)) the second, and =COUNTA(Thurlby!A2:A10000) against the stacked count the third.

Three cells, against £12,565.25 that nobody could explain and £30,785.25 that was never in the right place to begin with.

Share this article:
Back to Blog
Daily challenge · Day 74

Build a sorted, duplicate-free category breakdown from a ticket log with UNIQUE, SORT and COUNTIF

A new exercise every day, solved in a real grid.

Solve today’s challenge