Anyone who has kept a running total in Excel knows the sliding anchor trick: =SUM($A$2:A2) in the first cell, then drag it down forever. It works, until someone inserts a row, sorts the column, or the sheet grows to fifty thousand entries and you notice the recalculation stutter. The SCAN function replaces the entire column of copied formulas with one dynamic-array expression that spills down the sheet on its own. It also unlocks a few things the anchored SUM cannot do at all — per-category resets, running maximums, and streak counters, all from the same three arguments.
How SCAN reads an array once, left to right
SCAN walks through an array from top to bottom (or left to right, if the range is horizontal) and returns the intermediate values it produces along the way. That is the whole idea: REDUCE returns only the final answer, SCAN hands back every step. The formula is a LAMBDA that receives two names — the accumulated value so far, and the current cell — and returns whatever the next accumulator should be.
=SCAN(0, A2:A10, LAMBDA(acc, val, acc + val))
The three arguments are the initial value (0), the array to walk (A2:A10), and the LAMBDA that says how to combine the running accumulator with each new value. See the Microsoft Learn SCAN reference for the full signature. If you have used LAMBDA to build custom functions, the accumulator pattern will look familiar — the two parameter names are just acc and val by convention. Any names work.
Walking a five-cell column of 10, 20, 30, 40, 50 with an initial value of 0, the accumulator picks up each value in turn:
| A | B | C | |
|---|---|---|---|
| 1 | Value | acc going in | acc + val (spilled) |
| 2 | 10 | 0 | 10 |
| 3 | 20 | 10 | 30 |
| 4 | 30 | 30 | 60 |
| 5 | 40 | 60 | 100 |
| 6 | 50 | 100 | 150 |
A running total in one formula, no dragging
Say you have daily sales in B2:B32 and you want the running total in column C. The old pattern needs an anchor and a drag; SCAN needs one cell. Put the formula in C2 and it spills down the full 31 rows automatically.
=SUM($B$2:B2) (in C2, dragged to C32)
Breaks when rows are inserted above the formula range, and every cell recalculates independently.
=SCAN(0, B2:B32, LAMBDA(a,v,a+v))
One cell, spills to fit the range, survives row inserts inside B2:B32.
The formula reads: start the accumulator at 0, walk every value in B2:B32, and at each step return the previous accumulator plus the current value. Because SCAN spills, the output length always matches the input length — there is no such thing as one row of the running total lagging behind the sales column.
SCAN with a spilling source range like FILTER(B2:B1000, B2:B1000<>"") and the running total automatically stops at the last non-blank row — no drag, no manual trimming.
Reset the accumulator on each category
Anchored SUM cannot restart mid-column. SCAN can — the LAMBDA sees the current row’s value, so it can check whether a group boundary was crossed and reset the accumulator to zero. This is the pattern for a per-day, per-region, or per-order-id running total in a single formula.
Suppose column A holds a region name and column B holds a sale amount. To reset the running total every time the region in column A changes from the previous row, feed SCAN a two-column array and let the LAMBDA read both fields:
=SCAN(
0,
SEQUENCE(ROWS(A2:A50)),
LAMBDA(acc, i,
IF(INDEX(A2:A50,i)=INDEX(A2:A50,i-1),
acc + INDEX(B2:B50,i),
INDEX(B2:B50,i))))
The trick: instead of scanning the values themselves, scan a row-index sequence and let the LAMBDA look up both the region and the amount by row. If the region on row i matches the region on row i-1, add the current sale to the running total; otherwise start fresh with the current sale. Sort the sheet by region first — SCAN only compares neighbors, so a region that appears in two blocks gets two separate running totals.
| A | B | C | |
|---|---|---|---|
| 1 | Region | Sale | Running (per region) |
| 2 | East | 120 | 120 |
| 3 | East | 80 | 200 |
| 4 | West | 200 | 200 |
| 5 | West | 50 | 250 |
| 6 | West | 75 | 325 |
Wrap the whole thing in LET if you want to name the region range and the sale range once instead of typing A2:A50 four times. It reads dramatically better.
Handle errors before they poison the whole array
SCAN has one nasty failure mode: a single error in the source range breaks every downstream cell in the output. Once the accumulator absorbs a #DIV/0!, every subsequent row inherits it. The traditional dragged formula only breaks the specific row containing the bad value; SCAN propagates the error forward forever.
#N/A at row 12 turns rows 12 through the end of the spill into #N/A. Filter or coerce errors inside the LAMBDA — never trust the source range to be clean.
Neutralize errors with IFERROR on the incoming value. The accumulator only ever sees numbers, so the running total keeps flowing across the bad row:
=SCAN(0, B2:B32, LAMBDA(a, v, a + IFERROR(v, 0)))
The IFERROR(v, 0) swaps any error in the current cell for a zero before it hits the accumulator. Row 12 shows the running total unchanged from row 11, which is usually the intent; if you would rather flag those rows, replace the zero with a marker column instead. Either way, the failure stays contained to one row.
The classic anchored SUM still has its place
Not every running total should be a SCAN. If the workbook has to open on Excel 2019 or 2021, SCAN simply is not there — it needs Microsoft 365 or Excel 2024. Downgrading a file with SCAN in it produces #NAME? everywhere the function appears. Check the audience before you swap.
| Approach | Best when | Version gate |
|---|---|---|
=SUM($A$2:A2) anchor, dragged |
Small ranges, mixed-version audience, casual sheet | Any modern Excel |
SCAN one-shot spill |
Dynamic data, per-category resets, error handling in one place | Microsoft 365 / Excel 2024 |
| Power Query grouped running total | Refreshable pipelines, 100k+ rows, external sources | Any modern Excel |
The dragged SUM also wins for auditors who read one row at a time — every cell is a self-contained expression they can inspect in the formula bar. SCAN pushes that logic up into a single LAMBDA in row 2, which is elegant if you wrote it and opaque if you inherited the sheet. Add a comment cell next to the spill so the next person knows what they are looking at.
When SCAN is the wrong tool
SCAN handles well up to about 50,000 rows on a laptop before recalculation becomes noticeable. Beyond that, every edit to the source range recalculates the entire scan — the whole array, top to bottom. If the sheet is a live-updating log with tens of thousands of rows, a GROUPBY summary or a Power Query staging query will feel snappier than a giant spilling SCAN.
Check the fit before you commit to a rewrite:
- ✓ Source range is under ~50,000 rows and edited a few times a day, not a few times a second
- ✓ Every reader of the file has Microsoft 365 or Excel 2024
- ✓ The accumulator logic can be written in one line — nested
IFchains inside a LAMBDA get illegible fast - ✓ The output is a running total, running max, streak, or cumulative concatenation — not a full pivot
- ✓ Errors in the source range are handled inside the LAMBDA, not left to poison the spill
If two of those five feel wrong, keep the dragged formula or move to Power Query. SCAN is a scalpel; the anchored SUM is a butter knife. Both cut.
The lowest-risk swap is a monthly sales log or a daily-metrics tab — a single column of numbers, a few hundred rows, one reader. Replace the dragged SUM with =SCAN(0, range, LAMBDA(a,v,a+v)), watch the spill fill the column, then add IFERROR around the value once you find the first row that breaks it. From there, the per-category reset pattern extends naturally to any grouped log you already keep. Once the sheet has more than one running metric — a total and a running max, say — wrapping both scans in LET keeps the range definitions in one place and stops the formula bar from becoming a puzzle. That is when SCAN pays for the switch.
