Aether · Internal handbook

Excel formulas, explained properly

A practical guide to writing, reading and fixing spreadsheet formulas — from the basics through to dynamic arrays. Every example is interactive: click a cell to see its formula, step through nested logic, or paste your own formula into the formatter.

Audience everyone, beginner to advanced Excel version Microsoft 365 (legacy alternatives shown) Created by Jack Bahou with Claude · September 2026
How to use this handbook
  • Jump around. Use the sidebar, or press Ctrl+K and type a function name (e.g. xlookup, textjoin).
  • Searching moves you through the guide. Every mention is highlighted; Enter jumps to the next one, Shift+Enter the previous, Esc clears. The sidebar becomes a map of results — each section shows how many matches it holds, sections with none are greyed out, and clicking one takes you straight to its first match.
  • Tags: Basic everyone should know · 365 needs Microsoft 365 / Excel 2021+ · Legacy still works, but there's a better modern option · Advanced power-user territory.
  • Grids with a green formula bar are live. Click any cell (or use arrow keys) to see its formula; the ranges it uses light up in matching colours.
  • Copy buttons on every formula block give you a version you can paste straight into Excel.
Chapter 1

Formula fundamentals

Five minutes on the mechanics that every other chapter relies on: what the parts of a formula are called, how references behave when you copy a formula, and the handful of keystrokes that make editing painless.

Anatomy of a formula

Every formula starts with =. After that, Excel reads a mix of functions (named operations like SUM), references (cells or ranges like C2:C5), constants (numbers, "text in quotes", TRUE) and operators (+ - * / ^ &). Functions take arguments inside brackets, separated by commas.

=ROUND(SUM(C2:C5) * $F$1 / 1000, 2) & " tCO2e"
// ROUND(...)   function with two arguments: the value, and 2 decimal places
// SUM(C2:C5)   a function nested inside another function
// $F$1         an absolute reference (see below) — won't move when copied
// & " tCO2e"   the join operator, glueing text onto the number

Click around this grid to see how a small emissions table is built. The dot in a cell's corner means it holds a formula; the ranges it uses light up.

Words you'll see throughout this handbook
  • Range — a block of cells, written top-left:bottom-right, e.g. B2:D9. A whole column is B:B.
  • Argument — one input to a function. Optional arguments are shown in [square brackets] in Excel's tooltip.
  • Nesting — using a function's result as another function's argument: ROUND(SUM(...), 2).
  • Array — several values handled at once, written {1,2,3} (across) or {1;2;3} (down). Modern Excel returns arrays by "spilling" into neighbouring cells (chapter 7).
  • Volatile — a function that recalculates every time anything changes (NOW, OFFSET, INDIRECT, RAND). Fine in small doses; slow in big workbooks.

Relative, absolute and mixed references Basic

When you copy a formula, Excel shifts every reference by the same distance you moved. A $ pins a column letter or row number so it doesn't move. This single idea is behind most "why did my formula break when I filled it down?" questions.

Written asNameCopied one column right & one row down becomesUse when
A1RelativeB2Each copy should look at "its own" row/column — the default.
$A$1Absolute$A$1Everything should point at one fixed cell (a rate, a factor, a date).
$A1Mixed (column locked)$A2Always read from column A, but follow the row — e.g. a row label.
A$1Mixed (row locked)B$1Always read from row 1, but follow the column — e.g. a column header.
Keyboard: F4 cycles the dollars

Click inside a reference in the formula bar and press F4 repeatedly: A1 → $A$1 → A$1 → $A1 → A1. Much faster than typing $ by hand.

Reference shifter

Type a formula for cell B2, then see what it becomes when filled across and down.

$Red parts are locked; blue parts move. The green cell is where you typed the formula.

The classic exercise: one formula in B2 that fills a whole multiplication grid. Click cells in different rows and columns to see how the same formula points at different inputs.

Common mistakes
  • Forgetting to lock a lookup table. =VLOOKUP(A2,F2:G20,2,0) filled down becomes F3:G21, F4:G22… and quietly loses the top rows. Write $F$2:$G$20 (or use an Excel Table — chapter 6).
  • Locking too much. $A$2*$B$1 copied anywhere still multiplies A2 by B1. Only lock the part that should stay put.
  • Moving vs copying. Cutting and pasting (Ctrl+X) does not shift references — it keeps them pointing at the original cells. Copying (Ctrl+C) does shift them.

Order of operations

Excel evaluates operators in a fixed order. When in doubt, add brackets — they cost nothing and make intent obvious to the next reader.

PriorityOperatorMeaningExample
1: , spaceReference operators (range, union, intersection)SUM(A1:A5,C1:C5)
2- (negation)Minus sign in front of a value=-2^2 gives 4 in Excel, not −4 — negation happens before the power
3%Percent=50% → 0.5
4^Exponent=2^10 → 1024
5* /Multiply, divide=6/2*3 → 9 (left to right)
6+ -Add, subtract=2+3*4 → 14
7&Join text="Total: "&2+3 → "Total: 5"
8= <> < > <= >=Comparison — returns TRUE/FALSE=A1>=100
The comparison trap

=A1>B1*2 works as you'd expect (multiply first, then compare). But =A1="Yes"&"No" is not comparing A1 to "YesNo" — the & runs first. And =A1=B1=C1 does not test that all three are equal; it compares A1=B1 (TRUE/FALSE) with C1. Use AND(A1=B1, B1=C1).

Editing shortcuts that pay for themselves

Edit the active cell in placeF2
Cycle $ on the selected referenceF4
Evaluate the selected part of a formula (press Esc to undo)F9
Show all formulas instead of values (toggle)Ctrl+`
New line inside a formula or cellAlt+Enter
Enter the same formula into every selected cellCtrl+Enter
Fill down / fill right from the cell above / leftCtrl+D / Ctrl+R
Jump to the cells this formula refers toCtrl+[
Jump backF5 then Enter
Insert function dialog with argument helpShift+F3
Expand the formula barCtrl+Shift+U
Abandon an editEsc
Write long formulas on several lines

Excel doesn't care about spaces or line breaks inside a formula. Press Alt+Enter in the formula bar to break after each argument — it's the single biggest readability win for complex formulas, and the formatter tool in chapter 2 will lay them out for you.

Chapter 2

Reading & untangling complex formulas

Most "complex" formulas are just three or four simple ones nested together. The skill is knowing where to start reading, and having a tool that lays the formula out so the structure is visible.

The inside-out method

Excel evaluates the innermost brackets first and works outwards — so read the formula the same way. In practice:

  1. Find the deepest bracket pair. Click just after any ( or ) in the formula bar and Excel briefly bolds its partner; brackets are also colour-coded by depth.
  2. Ask what that innermost piece returns — a number? a position? a TRUE/FALSE? an array? Write it down if it helps.
  3. Replace it mentally with its result and move one level out. Repeat until you reach the =.
  4. Check with F9. Select any self-contained piece in the formula bar and press F9 to see its actual value. Press Esc afterwards or you'll hard-code the result!

The three walk-throughs below do exactly this, one step at a time. Use the buttons or the ← → keys.

Worked walk-throughs

1. A lookup wrapped in error handling

The most common pattern in any workbook: find a value in another sheet, and show a friendly message if it's missing. Cell B2 holds "Diesel"; the Factors sheet lists fuels in column A and factors in column C.

2. Conditional sum with boolean arithmetic

A SUMPRODUCT that adds up column D only where two conditions are both true. It looks intimidating because it works on whole arrays at once — but each step is simple. The data (8 rows) is: fuel in B, kWh in C, tonnes CO₂e in D.

3. Dynamic-array chain: FILTER → UNIQUE → TEXTJOIN 365

Modern formulas often pipe one array-returning function into the next. Read the chain from the inside: filter the list, remove duplicates, join into one string. Uses the same grid as above (site names in column A).

Formula formatter tool

Paste any formula and it will be laid out one argument per line, indented by nesting depth, with matching brackets colour-coded. Hover a bracket to see its partner. The output uses only spaces and line breaks, so Copy formatted gives you something you can paste straight back into Excel — it will still calculate.

Formula formatter

Nothing is calculated — it only re-arranges the text.
Argument separator
Try:
Rules the formatter follows (so you can do it by hand)
  • Short, simple calls such as MATCH(A2,Names,0) stay on one line. Anything longer, or containing another function, is opened up.
  • Each argument of an opened-up function goes on its own line, indented one level deeper than the function name. The closing bracket lines up with the function name.
  • Text in quotes is never split, even if it contains commas or brackets.
  • Array constants {1,2;3,4} and structured references Table[[#Headers],[Col]] are kept intact.
  • Depth colours: the bracket colour tells you how deep you are. If the closing bracket's colour doesn't match what you expected, you've found the missing bracket.

Naming the pieces with LET 365

When a formula repeats the same sub-expression, or is simply too long to read, LET lets you give each piece a name — like variables in code. It also makes the formula faster, because each named piece is calculated once.

LET 365 Advanced

LET(name1, value1, [name2, value2, …], calculation)

Names come in pairs (name, value) and the last argument is the calculation that uses them. Names can refer to earlier names.

Before — repeated lookup, hard to scan
=IF(XLOOKUP(B2,Factors[Fuel],Factors[kgCO2e])=0,
    "check fuel",
    C2*XLOOKUP(B2,Factors[Fuel],Factors[kgCO2e])/1000)
After — one lookup, named
=LET(
    factor, XLOOKUP(B2, Factors[Fuel], Factors[kgCO2e]),
    tonnes, C2 * factor / 1000,
    IF(factor = 0, "check fuel", tonnes)
)

Reading a LET: skim the names first (factor, tonnes) — they tell you the story. Then read the final line, which is usually short. Only dig into the value expressions if something looks wrong.

Excel's built-in helpers
  • Formulas → Evaluate Formula steps through a formula exactly like the walk-throughs above, one piece at a time. Ideal for someone else's formula.
  • Formulas → Trace Precedents / Dependents draws arrows to the cells a formula uses (or the formulas that use this cell). Remove with Remove Arrows.
  • F2 on a formula cell colours every reference and outlines the matching ranges on the sheet — the same thing the interactive grids in this handbook do.
  • Ctrl+` flips the whole sheet to show formulas instead of values, so you can spot the one hard-coded number in a column of formulas.
Chapter 3

Text & dates

Reshaping the value itself: splitting site codes, cleaning up imports, building labels, and working with dates — which Excel stores as plain numbers, a fact behind most date oddities.

Text functions

A realistic clean-up job: site codes exported from another system as one messy string. Click through the formula cells.

TEXT Basic

TEXT(value, format_code)

Turns a number into text using a format code — the same codes you can type into Format Cells (Ctrl+1) → Custom, such as #,##0.0 or d mmm yyyy. Essential when building labels with &, because & ignores cell formatting.

="Total: "&TEXT(E6,"#,##0.0")&" tCO2e as at "&TEXT(TODAY(),"d mmm yyyy")
→ Total: 72.6 tCO2e as at 18 Sep 2026

The result is text — you can't sum it. Keep the number in its own cell and only use TEXT for the label.

LEFT · RIGHT · MID Basic

LEFT(text, [num_chars]) · RIGHT(text, [num_chars]) · MID(text, start, num_chars)

Take characters from the start, end, or middle (starting at position start, counting from 1).

=LEFT("OX-01 Oxford Office", 5)     → OX-01
=RIGHT("2026-09", 2)               → 09
=MID("OX-01 Oxford Office", 4, 2)  → 01

LEN Basic

LEN(text)

Number of characters, including spaces. Handy for validation (=LEN(A2)=8) and for the classic "count how many times X appears" trick:

=LEN(A2)-LEN(SUBSTITUTE(A2,",",""))   // number of commas in A2

FIND · SEARCH Basic

FIND(find_text, within_text, [start]) · SEARCH(same arguments)

Return the position of one string inside another. FIND is case-sensitive and literal; SEARCH ignores case and allows wildcards (* ?). Both return #VALUE! if not found — wrap with ISNUMBER to make a test:

=ISNUMBER(SEARCH("gas", B2))   → TRUE if B2 contains "gas" in any case
=MID(A2, FIND(" ", A2)+1, 99)  // everything after the first space (legacy way)

SUBSTITUTE · REPLACE

SUBSTITUTE(text, old_text, new_text, [instance]) · REPLACE(text, start, num_chars, new_text)

SUBSTITUTE swaps text you know (all instances, or just the n-th). REPLACE swaps by position. Nine times out of ten you want SUBSTITUTE.

=SUBSTITUTE(A2, "-", "")        → OX01 Oxford Office
=SUBSTITUTE(A2, " ", "_", 1)    → OX-01_Oxford Office   (only the first space)

TRIM · CLEAN Basic

TRIM(text) · CLEAN(text)

The first thing to reach for when a lookup "should" match but doesn't. TRIM removes leading/trailing spaces and collapses doubles; CLEAN strips non-printing characters from imports. They don't remove non-breaking spaces (CHAR(160)) from web copy-paste — add SUBSTITUTE(x, CHAR(160), " ") for those.

=TRIM(CLEAN(SUBSTITUTE(A2, CHAR(160), " ")))   // the full "sanitise an import" wrapper

UPPER · LOWER · PROPER Basic

UPPER(text) · LOWER(text) · PROPER(text)

Change case. PROPER capitalises every word (so "o'neil" becomes "O'Neil" and "LPG" becomes "Lpg" — check your acronyms). Excel comparisons with = are already case-insensitive, so you rarely need these for matching.

& · CONCAT · CONCATENATE Basic

text1 & text2 · CONCAT(text1, [text2, …])

Join pieces of text. The & operator is the clearest for a few pieces; CONCAT accepts whole ranges. CONCATENATE is the old name, kept for compatibility.

=A2&" – "&B2&" ("&TEXT(C2,"#,##0")&" kWh)"
→ Oxford office – Electricity (48,200 kWh)

TEXTJOIN 365

TEXTJOIN(delimiter, ignore_empty, text1, [text2, …])

Join a whole range with a separator between items — the function everyone wanted for 20 years. Pair it with FILTER or UNIQUE to build "list all the sites in this region" strings.

=TEXTJOIN(", ", TRUE, A2:A5)               → Oxford office, Oxford office, Madrid office, Warehouse
=TEXTJOIN(", ", TRUE, UNIQUE(A2:A5))       → Oxford office, Madrid office, Warehouse

TEXTSPLIT 365

TEXTSPLIT(text, col_delimiter, [row_delimiter], [ignore_empty], [match_mode], [pad_with])

The formula version of Text-to-Columns. Splits into columns (and optionally rows) and spills the result.

=TEXTSPLIT("OX-01 Oxford Office", " ")   → OX-01 | Oxford | Office   (3 cells across)
=TEXTSPLIT("a,b;c,d", ",", ";")          → a b / c d   (2 × 2 block)

TEXTBEFORE · TEXTAFTER 365

TEXTBEFORE(text, delimiter, [instance_num], [match_mode], [match_end], [if_not_found]) · TEXTAFTER(same)

Replace the old LEFT(A2,FIND(" ",A2)-1) gymnastics. A negative instance_num counts from the end.

=TEXTBEFORE(A2, " ")          → OX-01
=TEXTAFTER(A2, " ")           → Oxford Office
=TEXTAFTER(A2, " ", -1)       → Office        (after the LAST space)
=TEXTBEFORE(A2, "-", , , , "no dash")   // fallback instead of #N/A

VALUE · NUMBERVALUE

VALUE(text) · NUMBERVALUE(text, [decimal_sep], [group_sep])

Convert text that looks like a number into a real number (green-triangle "number stored as text" cells won't sum until you do). NUMBERVALUE handles European "1.234,5". Quick alternatives: --A2, A2*1, or Data → Text to Columns → Finish on the whole column.

REPT

REPT(text, number_times)

Repeat text. The classic use is an in-cell bar chart: =REPT("█", ROUND(E2/5,0)). Also for padding codes: =REPT("0", 6-LEN(A2))&A2 (or just use the 000000 number format).

Common mistakes with text
  • Numbers that are secretly text. They left-align, get a green triangle, and are ignored by SUM. Fix with VALUE / -- / Text to Columns, and check the source if it keeps happening.
  • Invisible characters. Two cells look identical but =A1=B1 says FALSE. Compare LENs; use CODE(MID(A1,n,1)) to find the culprit; fix with the TRIM/CLEAN/SUBSTITUTE wrapper above.
  • Dates as text. "18/09/2026" typed in a text-formatted cell isn't a date. DATEVALUE converts it — but check day/month order.
  • Leading zeros vanishing. Postcodes and IDs like 007 lose zeros the moment they become numbers. Store as text, or use the 000 number format.

Dates are just numbers

Excel stores a date as the number of days since 1 January 1900 (18 Sep 2026 is 46,283); a time is the fraction of a day (12:00 is 0.5). That's why you can subtract dates to get a day count, add 7 to move a week on, and why a date formatted as a number suddenly looks like 46283.

DATE · TODAY · YEAR / MONTH / DAY Basic

DATE(year, month, day) · TODAY() · YEAR(date)

Build a date from parts, or pull parts out. DATE is forgiving: DATE(2026,13,1) is 1 Jan 2027 and DATE(2026,3,0) is the last day of February — a neat trick for "last day of previous month".

=DATE(YEAR(A2), 1, 1)     // first day of A2's year
=DATE(YEAR(A2), MONTH(A2)+1, 0)   // last day of A2's month (or use EOMONTH)

EOMONTH · EDATE Basic

EOMONTH(start_date, months) · EDATE(start_date, months)

Move whole months forward or back. EOMONTH lands on the month end; EDATE keeps the same day. Negative months go backwards; 0 means "this month".

=EOMONTH(A2, 0)      // end of A2's month       → 30/09/2026
=EOMONTH(A2, -1)+1   // first day of A2's month → 01/09/2026
=EDATE(A2, 12)       // one year on, same day   → 18/09/2027

NETWORKDAYS · WORKDAY

NETWORKDAYS(start, end, [holidays]) · WORKDAY(start, days, [holidays])

Count working days between two dates, or find the date n working days away. Point holidays at a list of bank-holiday dates. The .INTL versions let you choose which days count as the weekend.

DATEDIF Hidden

DATEDIF(start, end, "Y" | "M" | "D" | "YM" | "MD" | "YD")

Whole years/months/days between two dates. It's not in Excel's autocomplete (a compatibility leftover) but it works and it's the cleanest way to get "age in years" or "months elapsed".

=DATEDIF(A2, TODAY(), "M")   // whole months since A2
Date pitfalls
  • UK vs US order. A CSV with 03/04/2026 may be imported as 4 March. Check with =TEXT(A2,"d mmmm yyyy") and fix at import (Data → From Text/CSV lets you set the locale).
  • Dates with times. A "date" of 46283.6 has a time component; =A2=DATE(2026,9,18) fails. Compare INT(A2), or use >= and < the next day in SUMIFS.
  • Text dates in criteria. In SUMIFS write ">="&DATE(2026,1,1), not ">=01/01/2026" — the latter depends on regional settings.
Chapter 4

Logic & conditionals

Making a formula decide. The IF family covers "if this then that"; AND/OR combine tests; and the boolean-maths trick turns TRUE/FALSE into 1/0 so you can count and filter without any IF at all.

IF, IFS and SWITCH

IF Basic

IF(logical_test, value_if_true, [value_if_false])

Test something; return one value if it's TRUE, another if FALSE. Leaving out the third argument returns FALSE (usually not what you want — use "" for blank).

=IF(C2>50000, "Large", "Small")
=IF(C2="", "", C2*D2/1000)          // stay blank until there's an input
=IF(AND(B2="Natural gas", C2>50000), C2*0.18293/1000, 0)

IFS 2019+

IFS(test1, value1, [test2, value2, …])

Several tests in order; returns the value for the first test that's TRUE. Replaces nested IFs. There's no built-in "else" — add a final TRUE, "otherwise" pair, or you'll get #N/A when nothing matches.

Nested IF Legacy
=IF(C2>=100000,"A",IF(C2>=50000,"B",IF(C2>=10000,"C","D")))
IFS — same logic, readable
=IFS(
    C2 >= 100000, "A",
    C2 >= 50000,  "B",
    C2 >= 10000,  "C",
    TRUE,         "D"
)

Order matters. Tests are checked top to bottom, so put the strictest first (≥100000 before ≥50000). For banding with many tiers, a lookup table with XLOOKUP(…, -1) is cleaner still — see the XLOOKUP explorer.

SWITCH 2019+

SWITCH(expression, value1, result1, [value2, result2, …], [default])

When you're comparing one thing against a list of exact values, SWITCH is tidier than IFS because you write the expression once.

=SWITCH(B2, "Natural gas", "kWh", "Diesel", "litre", "Electricity", "kWh", "check unit")

AND, OR, NOT and boolean maths

AND · OR · NOT Basic

AND(test1, [test2, …]) · OR(test1, [test2, …]) · NOT(test)

AND is TRUE only if every test is TRUE; OR if any is; NOT flips. They return a single TRUE/FALSE, which is why they don't work inside array formulas like SUMPRODUCT or FILTER — there you multiply instead (below).

=IF(AND(C2>0, D2>0), C2*D2, "missing input")
=IF(OR(B2="Diesel", B2="Petrol"), "liquid fuel", "other")
=NOT(ISBLANK(A2))

Boolean maths: * for AND, + for OR, -- to convert Advanced

Excel treats TRUE as 1 and FALSE as 0 the moment you do arithmetic on it. So:

You writeIt meansWhy
(B2:B9="Natural gas")*(C2:C9>50000)AND, row by row1×1 = 1 only when both are TRUE
(B2:B9="Diesel")+(B2:B9="Petrol")OR, row by row0+1 or 1+0 gives 1 (both TRUE would give 2 — fine for FILTER, wrong for counting)
--(A2:A9<>"")Convert TRUE/FALSE to 1/0Two minus signs: negate, then negate back. *1 or N() do the same.
SUMPRODUCT(--(A2:A9>0))Count of positivesSums the 1s

This is the engine under SUMPRODUCT and the include argument of FILTER. Once you can read (cond1)*(cond2) as "cond1 AND cond2", most scary array formulas become plain English.

ISBLANK · ISNUMBER · ISTEXT · ISERROR · ISNA

ISNUMBER(value) etc.

Tests that return TRUE/FALSE about what a cell contains — useful as the test inside IF, or to build a "contains" check: =ISNUMBER(SEARCH("gas",B2)). Note ISBLANK is FALSE for a cell containing "" from a formula; use A2="" to catch both.

IFERROR vs IFNA — handle errors on purpose

IFERROR · IFNA Basic

IFERROR(value, value_if_error) · IFNA(value, value_if_na)

Both return value normally and a fallback if it errors. The difference is what they catch:

IFNA — catches only #N/A

"Not found" from lookups. Every other error (#REF!, #VALUE!, #DIV/0!) still shows, so genuine bugs stay visible. Prefer this for lookups.

IFERROR — catches everything

Including the error that tells you a column was deleted or a number is text. Use it only when you're sure any error should be silenced.

=IFNA(XLOOKUP(A2, Factors[Fuel], Factors[Factor]), "check fuel name")
=IFERROR(C2/D2, 0)          // OK: the only realistic error is divide-by-zero
Common mistakes with logic
  • Wrapping everything in IFERROR(…, 0). A broken reference now silently reports zero and your totals are quietly wrong. Return a visible message instead, or use IFNA.
  • Comparing to text numbers. =IF(A2="10",…) compares with the text "10". Write =IF(A2=10,…).
  • Returning "" then doing maths. A cell showing blank from IF(x,"",…) is text; =B2+1 gives #VALUE!. Use SUM (which ignores text) or return 0 and hide it with the 0;-0;"-" number format.
  • IFS with no default. Ends in #N/A for unexpected inputs. Add TRUE, "unexpected" as the last pair.
Chapter 5

Lookup & reference

"Find this fuel in the factors table and give me its factor." This chapter starts with the way most of us learned it (VLOOKUP), shows the more robust classic (INDEX/MATCH), then makes the case for XLOOKUP — which does everything both do, with fewer traps.

VLOOKUP — the one everyone learned first Legacy

VLOOKUP Basic Legacy

VLOOKUP(lookup_value, table_array, col_index_num, [range_lookup])

Looks for lookup_value down the first column of table_array, then returns the value from column number col_index_num of that table (counting the first column as 1). The fourth argument must be FALSE (or 0) for an exact match — always set it; the default is approximate matching, which returns wrong answers silently on unsorted data.

=VLOOKUP(A2, $F$2:$H$6, 3, FALSE)
// A2            what to find ("Diesel")
// $F$2:$H$6     the table, locked so it doesn't shift when filled down
// 3             return the 3rd column of that table (Factor)
// FALSE         exact match — never leave this out
Why VLOOKUP causes so many problems
  • It can only look right. The key must be in the first column of the table. If the fuel name is in column H and the factor in column F, you're stuck (or you re-arrange the source data).
  • The column number is hard-coded. Insert a column inside the table and 3 now points at the wrong thing — no error, just wrong numbers.
  • Approximate match by default. Forget FALSE and "Kerosene" may return Diesel's factor because it's alphabetically nearby.
  • Unlocked ranges. F2:H6 filled down becomes F3:H7 and the top row drops out of the lookup.
  • Slow on big data. Returning several columns means several VLOOKUPs, each scanning the whole table.

HLOOKUP Legacy

HLOOKUP(lookup_value, table_array, row_index_num, [range_lookup])

VLOOKUP rotated 90°: searches the first row and returns from a row number below. Same limitations. XLOOKUP handles both directions.

INDEX & MATCH — the robust classic

Two small functions that, combined, fix every VLOOKUP problem above. MATCH finds the position of a value in a single row or column. INDEX returns the value at a given position in a range. Chain them: "find which row Diesel is on, then return that row of the Factor column".

MATCH Basic

MATCH(lookup_value, lookup_array, [match_type])

Returns a position (1, 2, 3…), not a value. match_type 0 = exact (use this), 1 = largest value ≤ lookup (list sorted ascending), -1 = smallest value ≥ lookup (sorted descending).

=MATCH("Diesel", F2:F6, 0)   → 2       // Diesel is the 2nd item

INDEX Basic

INDEX(array, row_num, [column_num])

Returns the cell at that row (and column) of the range. Row 0 or column 0 means "the whole row/column", which is how INDEX can return a range for other functions.

=INDEX(H2:H6, 2)             → 2.51233   // 2nd cell of the Factor column
=INDEX(G2:I4, 2, 3)          → value at row 2, column 3 of the block

INDEX / MATCH together, and the two-way lookup Advanced

=INDEX($H$2:$H$6, MATCH(A2, $F$2:$F$6, 0))
// read it inside-out: MATCH finds the row → INDEX returns that row of column H
// key column and return column are separate, so the key can be ANYWHERE — left, right, another sheet

Use two MATCHes for a grid where you need a row and a column — here, emission factors by fuel (rows) and year (columns):

=INDEX($G$2:$I$4, MATCH(A2, $F$2:$F$4, 0), MATCH(B2, $G$1:$I$1, 0))
// INDEX(block, which row, which column)

Inserting columns in the source table no longer breaks anything, because the return column is a reference, not a number. This pattern is still worth knowing: it's everywhere in existing workbooks, and it works in every Excel version.

XLOOKUP — the modern replacement 365

XLOOKUP is INDEX/MATCH in a single function, with a built-in "if not found", the ability to return several columns at once, and search-direction and match-mode options. If your whole team is on Microsoft 365, it should be your default.

XLOOKUP 365 Basic

XLOOKUP(lookup_value, lookup_array, return_array, [if_not_found], [match_mode], [search_mode])

ArgumentWhat it isValues
lookup_valueWhat to findA cell, text, number, or an array of several
lookup_arrayWhere to search (one row or one column)e.g. Factors[Fuel]
return_arrayWhat to return — same height as lookup_arrayOne column, or several (Factors[[Unit]:[Factor]]) to spill them all
if_not_foundValue to show instead of #N/A"check name", 0, ""
match_modeHow to match0 exact (default) · -1 exact or next smaller · 1 exact or next larger · 2 wildcard
search_modeWhere to start1 first to last (default) · -1 last to first · 2/-2 binary search (sorted data only)
=XLOOKUP(A2, $F$2:$F$6, $H$2:$H$6, "not found")
=XLOOKUP(A2, Factors[Fuel], Factors[Factor], "not found")          // same, using a Table
=XLOOKUP(A2, Factors[Fuel], Factors[[Unit]:[Factor]], "")          // returns Unit AND Factor, spilling across
=XLOOKUP(A2, Factors[Fuel], Factors[Factor], , , -1)               // last match (e.g. latest entry in a log)

The same job, three ways

Find the factor for the fuel in A2 from a table with Fuel in F and Factor in H:

VLOOKUP Legacy
=VLOOKUP(A2,$F$2:$H$6,3,FALSE)

Key must be first column; 3 is fragile; no not-found handling.

INDEX / MATCH
=INDEX($H$2:$H$6,MATCH(A2,$F$2:$F$6,0))

Robust and universal, but two functions to read.

XLOOKUP 365
=XLOOKUP(A2,$F$2:$F$6,$H$2:$H$6,"not found")

Reads left to right: find this, here, return that.

CapabilityVLOOKUPINDEX / MATCHXLOOKUP
Look left of the key columnNoYesYes
Survives inserting columns in the tableNoYesYes
Exact match by defaultNoNoYes
Built-in "if not found"NoNoYes
Return several columns at onceNoPartlyYes
Search from the bottom (last match)NoNoYes
Next-smaller / next-larger bandingSorted onlySorted onlyAny order
Horizontal lookupHLOOKUPYesYes
Works in Excel 2016 / 2019YesYesNo

XLOOKUP explorer

Change the lookup value and the two mode arguments to see what XLOOKUP returns and why. Type a fuel name (try a wildcard like *gas with match mode 2), or a number to switch to a banding table where the "next smaller" mode shines.

Try XLOOKUP's modes

Real XLOOKUP logic running on two small tables.
Try:

        

Other reference functions

XMATCH 365

XMATCH(lookup_value, lookup_array, [match_mode], [search_mode])

MATCH with XLOOKUP's mode arguments and exact-match default. Use with INDEX when you need a position (e.g. to feed into OFFSET or to find the last non-blank row: =XMATCH(TRUE, A:A<>"", , -1)).

Approximate matching for bands and tiers

When you have thresholds (0, 50, 100, 250, 500 → Band A–E), don't write nested IFs. Put the lower bounds in a column and use "exact or next smaller":

=XLOOKUP(C2, Bands[Threshold], Bands[Band], "n/a", -1)   // 365: works in any order
=VLOOKUP(C2, $F$2:$G$6, 2, TRUE)                        // legacy: thresholds must be sorted ascending

INDIRECT Advanced

INDIRECT(ref_text, [a1])

Turns text into a reference: =INDIRECT("'"&A2&"'!B5") reads B5 from whichever sheet is named in A2. Powerful for "one summary row per sheet" layouts. Costs: it's volatile (recalculates constantly), it breaks silently if a sheet is renamed, and Excel can't trace it. Use sparingly; consider a Table plus XLOOKUP, or Power Query, instead.

OFFSET Advanced

OFFSET(reference, rows, cols, [height], [width])

Returns a range that's shifted from a starting cell and optionally resized. Classic for "the last 12 months": =SUM(OFFSET(B1, COUNTA(B:B)-12, 0, 12, 1)). Also volatile. In 365, TAKE/DROP (=SUM(TAKE(B2:B200,-12))) do the same job without the volatility.

CHOOSE · ROW · COLUMN

CHOOSE(index_num, value1, [value2, …]) · ROW([ref]) · COLUMN([ref])

CHOOSE(3,"Q1","Q2","Q3","Q4") returns "Q3". ROW() and COLUMN() return the current row/column number — handy for alternating formats or building sequences in older Excel (365 has SEQUENCE).

Referring to other sheets and workbooks

=Factors!B2                         // sheet named Factors, cell B2
='Site data 2026'!B2                // sheet names with spaces need single quotes
=SUM(Jan:Dec!B2)                    // 3-D reference: B2 on every sheet from Jan to Dec
='C:\Reports\[Factors 2026.xlsx]Factors'!$B$2   // another workbook (a "workbook link", see chapter 6)

Tip: instead of typing these, start the formula, then click the other sheet tab and the cell — Excel writes the reference correctly, quotes and all.

Chapter 6

Tables, PivotTables, names & links

A short introduction to four features that make workbooks more robust: Excel Tables (ranges that know their own size), PivotTables (summaries without formulas), named ranges, and links between workbooks.

Excel Tables and structured references Basic

Select your data and press Ctrl+T. The range becomes a Table: it gets a name (rename it on the Table Design tab — "Table1" is not a name), filter buttons, banded rows, and it grows automatically when you add rows. Formulas that refer to the table pick up new rows with no editing.

Inside formulas you refer to a Table by name and column instead of cell addresses. These are called structured references:

SyntaxRefers toTypical use
EnergyAll data rows (no header, no total)=ROWS(Energy)
Energy[kWh]The data cells of one column=SUM(Energy[kWh])
[@kWh]This row's value in that column (only inside the table)=[@kWh]*[@Factor]/1000
Energy[@]The whole current rowrare
Energy[[Fuel]:[kWh]]Several adjacent columns=XLOOKUP(x, Energy[Site], Energy[[Fuel]:[kWh]])
Energy[[#Headers],[kWh]]The header celllabels, MATCH across headers
Energy[#Headers]All header cells=MATCH("kWh", Energy[#Headers], 0)
Energy[#Totals] · Energy[[#Totals],[kWh]]The total row (if switched on)referencing the visible total
Energy[#All]Everything including headers and totalscharts, copying
Energy[#Data]Same as Energyexplicitness

Click through the formulas in and around this Table. Notice the formula in E2 is identical in every row — Excel calls this a calculated column; type it once and it fills the whole column, new rows included.

Why Tables beat plain ranges
  • No more $A$2:$A$500 that quietly stops at row 500 when the data reaches 600.
  • Formulas read like sentences: SUMIFS(Energy[tCO2e], Energy[Fuel], "Natural gas").
  • PivotTables and charts built on a Table refresh to include new rows.
  • Column headers stay visible when you scroll (they replace the A/B/C letters).
  • Caveat: Tables can't be used with merged cells or with sheet-level array formulas that need to spill into the Table. Keep spilling formulas outside.

PivotTables & PivotCharts

A PivotTable summarises a list — totals, counts, averages — by dragging fields into four zones, with no formulas. If you find yourself writing a grid of SUMIFS for every site × every fuel × every month, that's a PivotTable. Select your data (ideally a Table) and go to Insert → PivotTable.

Source list (Table) DateSiteFuelkWhtCO2e one row per reading (hundreds or thousands) Refresh: Alt+F5 FILTERSDate (Year = 2026) COLUMNSFuel ROWSSite VALUESSum of tCO2e drag fields Result Year: 2026 ▾ SiteElecGasTotal Oxford12.017.729.7 Madrid6.538.444.9 Warehouse–15.915.9 Total18.572.090.5 rows columns values
The four drop zones and where each one ends up in the finished PivotTable. Values are illustrative.
ZoneWhat goes thereBecomes
RowsCategories you want down the side (Site, Fuel, Month)One row per distinct value; nest several for a hierarchy
ColumnsCategories across the topOne column per distinct value
ValuesNumbers to aggregate (kWh, tCO2e)Sum by default; right-click → Summarize Values By for Count, Average, Max…
FiltersFields to slice the whole table byA dropdown above the table. Slicers (Insert → Slicer) are the nicer, clickable version.
PivotTable essentials
  • It doesn't update itself. After the source changes, right-click → Refresh (or Alt+F5). Build it on a Table so new rows are included.
  • Dates group automatically into Years / Quarters / Months. Right-click a date → Group to change.
  • Show Values As (right-click a value) gives % of total, difference from previous, running total — no formulas needed.
  • PivotChart (PivotTable Analyze → PivotChart) is a chart wired to the pivot: filter the pivot, the chart follows.
  • GETPIVOTDATA. Clicking a pivot cell from another formula writes =GETPIVOTDATA("tCO2e",$A$3,"Site","Oxford"). It's robust to the pivot re-arranging. If you'd rather get a plain =B5, switch it off: PivotTable Analyze → Options ▾ → untick Generate GetPivotData.
  • Blank cells in a Values column silently turn Sum into Count. Check the source column is all numbers.

Name Manager — naming cells and ranges

A defined name is a label for a cell, a range or a constant. =C2*ElecFactor/1000 reads better than =C2*Factors!$D$7/1000, and if the factor moves, you update the name in one place. Create one by selecting the cell and typing in the Name Box (left of the formula bar), or via Formulas → Define Name. Manage them all with Ctrl+F3.

NameRefers toUse
ElecFactor=Factors!$D$7=[@kWh]*ElecFactor/1000
ReportYear=2026 (a constant — no cell at all)=SUMIFS(…, Energy[Year], ReportYear)
FuelList=Factors[Fuel]Source for a Data Validation dropdown
kgToTonnes=LAMBDA(kg, kg/1000)A custom function: =kgToTonnes(B2) (365)
Rules and tips for names
  • Names can't contain spaces or look like a cell reference (Q1, FY2026 are invalid — use Qtr1, FY_2026).
  • Scope: workbook-scoped (default, usable anywhere) or sheet-scoped (only on that sheet; lets two sheets have their own Total). Set it when creating; it can't be changed afterwards.
  • Press F3 while editing a formula to paste a name from a list.
  • Name Manager's filter shows names with errors (#REF!) — leftovers from deleted sheets that slow files down. Delete them.
  • Data Validation lists and Conditional Formatting rules accept names; that's often the cleanest way to reference a Table from those dialogs.
Chapter 7

Data analysis & dynamic arrays

Summing and counting with conditions is the bread and butter of any reporting sheet. Then the modern part: dynamic-array functions that return whole lists — filtered, sorted, de-duplicated — from a single formula.

SUMIFS, COUNTIFS, AVERAGEIFS, MAXIFS, MINIFS Basic

All five share one shape: the range to aggregate first, then pairs of (criteria range, criterion). Every pair must be TRUE for a row to count (an AND). Click through the summary formulas on the right of this grid.

SUMIFS · COUNTIFS · AVERAGEIFS · MAXIFS · MINIFS Basic

SUMIFS(sum_range, criteria_range1, criteria1, [criteria_range2, criteria2, …]) · COUNTIFS(criteria_range1, criteria1, …)

Use the plural …IFS versions even for one condition — the argument order is consistent and you can add conditions later. (SUMIF/COUNTIF are the older singular forms with the sum range last.)

=SUMIFS(Energy[kWh], Energy[Site], "Oxford office", Energy[Fuel], "Electricity")
=COUNTIFS(Energy[Fuel], "Natural gas", Energy[kWh], ">10000")
=AVERAGEIFS(Energy[kWh], Energy[Site], F2)
=MAXIFS(Energy[kWh], Energy[Fuel], "Natural gas")

Criteria syntax — the part people get wrong

Criteria are text strings, even when they compare numbers. Anything other than "equals this cell" needs quotes, and cell references are glued on with &:

CriterionMatches rows where the criteria cell is…
"Diesel" or F2equal to Diesel (case-insensitive)
"<>Diesel"not Diesel
">10000"greater than 10000
">="&G2≥ the value in G2 (note the &)
">="&DATE(2026,1,1)on or after 1 Jan 2026 — the safe way to write dates
"*gas*"contains "gas" (* = any characters, ? = one character)
"OX-??"OX- followed by exactly two characters
"<>"not blank
""blank
"~*"a literal asterisk (tilde escapes wildcards)

OR conditions aren't supported directly. Either add two SUMIFS together, or pass an array: =SUM(SUMIFS(D:D, C:C, {"Diesel","Petrol"})).

SUMPRODUCT and SUBTOTAL

SUMPRODUCT Advanced

SUMPRODUCT(array1, [array2, …])

Multiplies arrays element by element, then sums. Two jobs: (1) weighted sums in one go — =SUMPRODUCT(C2:C9, D2:D9) is Σ(kWh × factor) without a helper column; (2) conditional sums with logic SUMIFS can't express (OR across columns, conditions on calculated values, year of a date).

=SUMPRODUCT(Energy[kWh], Energy[Factor]) / 1000        // total tonnes, no helper column
=SUMPRODUCT((YEAR(Energy[Date])=2026) * (Energy[Fuel]="Natural gas") * Energy[kWh])
// (condition) * (condition) * values — see Boolean maths in chapter 4

Reading tip: each (…) in brackets is a column of TRUE/FALSE; the * turns them into 1/0 and combines them with AND. Step through the walk-through in chapter 2.

SUBTOTAL · AGGREGATE

SUBTOTAL(function_num, ref1, …) · AGGREGATE(function_num, options, ref1, …)

SUBTOTAL aggregates only the visible rows — so a total above a filtered Table changes as you filter. function_num 9 = SUM, 1 = AVERAGE, 3 = COUNTA; add 100 (109, 101, 103) to also ignore manually hidden rows. It also ignores other SUBTOTALs, so nested subtotals don't double count. The Table "Total Row" uses it.

AGGREGATE is SUBTOTAL's big sibling: 19 functions and an options code — 6 = ignore errors, which makes =AGGREGATE(9, 6, D2:D500) a "sum, skipping the #N/As" without touching the errors.

Dynamic arrays: formulas that return lists 365

In Microsoft 365, a formula that produces several values spills them into the cells below and/or to the right. You write one formula, in one cell; Excel draws a thin blue border around the results. Only the top-left cell holds the formula — the rest are read-only ghosts. Refer to the whole spilled range with a #: =COUNTA(E2#).

FILTER 365

FILTER(array, include, [if_empty])

Returns the rows of array where include is TRUE. include is a column of TRUE/FALSE — combine tests with * (AND) and + (OR). Always set if_empty or you'll get #CALC! when nothing matches.

=FILTER(A2:C9, B2:B9="Natural gas", "none")
=FILTER(Energy, (Energy[Fuel]="Natural gas") * (Energy[kWh]>50000), "none")
=FILTER(Energy, (Energy[Site]="Oxford office") + (Energy[Site]="Lab"), "none")   // OR

SORT · SORTBY 365

SORT(array, [sort_index], [sort_order], [by_col]) · SORTBY(array, by_array1, [order1, …])

SORT orders by a column inside the array (index number, 1 = ascending, -1 = descending). SORTBY orders by any other range, and can take several keys.

=SORT(FILTER(A2:C9, B2:B9="Natural gas"), 3, -1)     // gas rows, biggest kWh first
=SORTBY(Energy[Site], Energy[tCO2e], -1)              // sites ordered by emissions

UNIQUE 365

UNIQUE(array, [by_col], [exactly_once])

The distinct values, in order of first appearance. Feed it to SORT for an alphabetical list, or to COUNTA to count distinct items. exactly_once = TRUE returns only the values that appear a single time.

=SORT(UNIQUE(Energy[Site]))
=COUNTA(UNIQUE(Energy[Site]))          // how many distinct sites

SEQUENCE 365

SEQUENCE(rows, [columns], [start], [step])

Generates numbers. =SEQUENCE(12) → 1 to 12 down a column; =EDATE(DATE(2026,1,1), SEQUENCE(12,1,0)) → the 12 month-start dates of 2026 — the usual way to build a report header that never needs retyping.

TAKE · DROP · CHOOSECOLS · VSTACK / HSTACK 365 Newer

Array "plumbing": TAKE(x, -12) the last 12 rows; DROP(x, 1) skip the header; CHOOSECOLS(x, 1, 3) pick columns; VSTACK(a, b) append one list under another (e.g. combine two sites' Tables into one). They replace most OFFSET and INDEX-with-SEQUENCE tricks.

GROUPBY · PIVOTBY 365 Newest

GROUPBY(row_fields, values, function, […]) · PIVOTBY(row_fields, col_fields, values, function, […])

A PivotTable as a formula — it updates live, no Refresh. =GROUPBY(Energy[Site], Energy[tCO2e], SUM) gives a site total list; PIVOTBY adds column fields. Rolling out across 365 channels during 2024–25; check your version has it before building on it.

#SPILL! and other dynamic-array gotchas
  • #SPILL! means something is in the way — a value, a merged cell, or an Excel Table — where the results need to go. Click the cell and Excel outlines the blocked area.
  • Spilled formulas can't live inside a Table. Put them beside it.
  • @ appears in old formulas. When a pre-365 workbook opens, Excel adds @ (implicit intersection) where a formula would now spill. It's preserving the old behaviour; remove the @ only if you want spilling.
  • Colleagues on older Excel see spilled results as static values and _xlfn.FILTER in the formula bar. Fine for reading, broken for editing.
  • Whole-column references (A:A) inside FILTER/UNIQUE work but are slow — they process a million rows. Use a Table.

LET, LAMBDA and the helper functions Advanced

LET was covered in chapter 2. LAMBDA goes one step further and turns a formula into a reusable function with a name.

LAMBDA 365 Advanced

LAMBDA([parameter1, …], calculation)

Define it in Name Manager (Ctrl+F3 → New → Refers to) and call it by name anywhere in the workbook:

// Name: TONNES   Refers to:
=LAMBDA(qty, fuel, qty * XLOOKUP(fuel, Factors[Fuel], Factors[Factor]) / 1000)
// then in any cell:
=TONNES([@kWh], [@Fuel])

Good for a calculation that appears in ten places and must stay consistent. The downside: colleagues can't see the logic in the cell, so document the name's Comment field in Name Manager.

BYROW · BYCOL · MAP · SCAN 365 Advanced

Apply a LAMBDA to each row, column, or element of an array and get an array back. =BYROW(B2:E9, LAMBDA(r, SUM(r))) gives per-row totals as one spilled formula; =SCAN(0, C2:C9, LAMBDA(acc, x, acc + x)) is a running total. Rarely essential — but they remove the need for helper columns.

Chapter 8

Errors & debugging

Every error value is Excel telling you something specific. Learn the ten messages and most "my formula is broken" moments take a minute rather than an afternoon.

Every error explained

ErrorExcel is sayingUsual causeFix
#N/A"Not available" — a lookup found nothingValue not in the list; trailing space; number vs text mismatch; VLOOKUP looking in the wrong columnCheck the key with =F2=A2 and LEN; TRIM the data; wrap with IFNA for a friendly message
#REF!A reference no longer existsYou deleted a row/column/sheet a formula pointed at; VLOOKUP's column number is bigger than the table; INDEX row out of rangeUndo if you can; otherwise re-point the reference. Tables and XLOOKUP avoid the column-number case
#VALUE!Wrong type of dataMaths on text ("abc"*2, or a "number" stored as text); a date typed as text; a function given a range where it wants one valueFind the text cell (ISTEXT, or Ctrl+` to show formulas); convert with VALUE or Text to Columns
#DIV/0!Division by zero (or by blank)Denominator is 0 or empty — typical in averages of empty periods=IF(D2=0, "", C2/D2) or IFERROR(C2/D2, "")
#NAME?Excel doesn't recognise a nameMisspelt function (SUMM); text without quotes; a defined name that doesn't exist; a 365-only function on older ExcelCheck spelling; add quotes; open Name Manager; check Excel version
#NUM!Invalid numberSquare root of a negative; date before 1900; result too large; IRR that can't convergeCheck inputs; use ABS where appropriate
#NULL!Two ranges don't intersectA space between references where a comma or colon was meant: SUM(A1 B1)Replace the space with , or :
#SPILL!A dynamic-array result has nowhere to goCells below/right are not empty; the formula is inside a Table; merged cells in the wayClear the area or move the formula; keep spills outside Tables
#CALC!The calculation engine can't produce a resultFILTER returned nothing and no if_empty was given; a LAMBDA with the wrong number of inputs; an array inside an arrayAdd the if_empty argument; check parameters
#GETTING_DATAStill fetchingData types / external queries still loadingWait; refresh if it persists
########Not an error — the column is too narrowNumber or date wider than the cell; or a negative date/timeDouble-click the column border to autofit; check for negative dates
Circular referenceA formula refers to its own cell (directly or via others)=SUM(A1:A10) placed in A10; or a chain of cells that loopsStatus bar shows the cell address; Formulas → Error Checking → Circular References lists them
Green triangle in the corner?

That's a warning, not an error. Hover the cell and click the ⚠ to see which: "Number stored as text", "Formula omits adjacent cells", "Inconsistent formula" (this cell differs from its neighbours — often the real bug). Convert or fix; use "Ignore Error" only when it really is intended.

Evaluate Formula — Excel's own step-by-step debugger Basic

The single most useful button for understanding a formula someone else wrote. Select the cell, then Formulas tab → Formula Auditing group → Evaluate Formula. A dialog opens showing the formula with one part underlined: that's the piece Excel will work out next. Press Evaluate and the underlined piece is replaced, in place, by its result. Keep pressing and you watch the formula collapse to its answer — exactly the inside-out reading from chapter 2, done for you.

ButtonWhat it doesUse it when
EvaluateWorks out the underlined part and replaces it with its valueEvery step — this is the one you'll press most
Step InOpens the formula of the referenced cell in a sub-panel, so you can evaluate that formula tooThe underlined part is a reference to another cell and you suspect the problem is there
Step OutReturns from that sub-panel with the referenced cell's value filled inYou've seen what the other cell produces
RestartStarts again from the original formula (the button becomes Restart once you reach the end)You clicked past the interesting bit

Here's the same process on a realistic formula. Cells C2:C5 hold 48,200 / 96,500 / 31,500 / 210,000 kWh and F1 holds the factor 0.18293. Step through it exactly as the dialog would:

Getting the most out of it
  • Watch which branch runs. An IF only evaluates the side that wins. If the answer looks wrong, check the test resolved the way you assumed — a broken formula in the branch that never runs stays invisible.
  • Italic text means "already calculated". Excel shows a value in italics when it came from a cell it has evaluated on a previous pass; it won't recalculate it.
  • It cannot step into everything. Step In is greyed out for references to another workbook, to a cell that contains a constant rather than a formula, and for the same reference a second time.
  • Nothing is changed. Unlike F9, Evaluate Formula never edits the cell — you can close it at any point with no risk.
Use Evaluate Formula when…

You want the whole story in order, you're reading someone else's work, an IF is picking the wrong branch, or the formula is long enough that you'd lose your place doing it by hand. Safe — it can't alter the cell.

Use F9 when…

You want one specific piece checked immediately: select just that part in the formula bar and press F9. Faster, but it writes the result into the formula — always press Esc afterwards, never Enter.

Debugging toolkit

  1. Read the error. The table above tells you which family of problem you have — don't start by rewriting the formula.
  2. Evaluate a piece. In the formula bar, select one function or bracket group and press F9. Look at the value. Esc to leave the formula unchanged. For the whole formula in order, use Evaluate Formula.
  3. Check the inputs, not the formula. Most lookup failures are data: a trailing space, "1001" as text vs 1001 as a number, a date with a hidden time. =LEN(A2), =ISNUMBER(A2), =A2=F5 answer these in seconds.
  4. Trace it. Formulas → Trace Precedents draws arrows from every input into this cell; Trace Dependents shows what breaks if you change it. Clear with Remove Arrows.
  5. Show formulas. Ctrl+` reveals a hard-coded number hiding in a column of formulas, or a formula that was pasted as a value.
  6. Find inconsistencies. Ctrl+G → Special → Row differences selects the cells whose formula differs from the first cell in each row.
  7. Isolate. Copy the formula to a scratch cell and remove one layer at a time (IFERROR first — it's hiding the real error).
  8. Check calculation mode. If nothing updates, Formulas → Calculation Options may be on Manual (someone set it to speed up a big file). F9 recalculates.

Finding invisible characters: CODE · UNICODE · CHAR

CODE(text) · UNICODE(text) · CHAR(number)

Two cells look identical but don't match. Compare lengths, then look at each character's code: 32 is a normal space, 160 is a non-breaking space from web pages, 10 is a line break, 9 a tab.

=LEN(A2)                         → 7    (but the visible text is 6 characters)
=UNICODE(RIGHT(A2,1))            → 160  (non-breaking space — TRIM won't remove it)
=SUBSTITUTE(A2, CHAR(160), "")   // fix

FORMULATEXT · ISFORMULA

FORMULATEXT(reference) · ISFORMULA(reference)

Show a neighbouring cell's formula as text (useful in documentation columns or for auditing a model), or test whether a cell contains a formula at all — combined with Conditional Formatting, =NOT(ISFORMULA(A1)) highlights every hard-coded value in a calculation block.

Silent errors — the dangerous kind

Formulas that don't show an error but give a wrong number are the ones that reach a client. The usual suspects:

  • VLOOKUP without FALSE returning a near-miss.
  • A SUM range that stops one row short of the data (check with the status-bar sum after selecting the whole column).
  • IFERROR(…, 0) hiding a broken reference.
  • SUMIFS criteria ranges of different sizes (they must all be the same shape).
  • A pasted value sitting in a column of formulas (Ctrl+` finds it).
  • Calculation set to Manual.
  • Percentages entered as 5 instead of 0.05 (or vice versa).
Chapter 9

Quick reference

The pages to keep open: shortcuts, a lookup decision flow, and an alphabetical index of every function in this handbook.

Keyboard shortcuts worth learning

Editing & formulas

Edit cellF2
Toggle $ (absolute/relative)F4
Evaluate selected part of formulaF9
Recalculate allF9 (or Ctrl+Alt+F9 to force)
Show / hide formulasCtrl+`
New line in cellAlt+Enter
Fill selection with formulaCtrl+Enter
Fill down / rightCtrl+D / Ctrl+R
AutoSumAlt+=
Insert functionShift+F3
Paste a defined nameF3
Name ManagerCtrl+F3
Go to precedentsCtrl+[
Go to dependentsCtrl+]

Navigation & selection

Jump to edge of dataCtrl+Arrow
Select to edge of dataCtrl+Shift+Arrow
Select current regionCtrl+A (once)
Select whole column / rowCtrl+Space / Shift+Space
Go To (cell, name, Special…)Ctrl+G or F5
Next / previous sheetCtrl+PgDn / PgUp
Home of sheetCtrl+Home
Last used cellCtrl+End

Data & formatting

Format Cells dialogCtrl+1
Create TableCtrl+T
Toggle filterCtrl+Shift+L
Insert / delete rows or cellsCtrl++ / Ctrl+-
Paste SpecialCtrl+Alt+V
Paste values onlyCtrl+Alt+V, V, Enter
Number / percent / date formatCtrl+Shift+1 / 5 / 3
Today's date / current timeCtrl+; / Ctrl+Shift+;
Flash Fill (pattern-based fill)Ctrl+E
Refresh PivotTable / queryAlt+F5 / Ctrl+Alt+F5 (all)
Insert chart from selectionAlt+F1
Freeze panes (Alt sequence)Alt, W, F, F

Which lookup should I use?

What do you need back? one value many matching rows On Microsoft 365? FILTER (+ SORT / UNIQUE) 365 only · spills a list yes no (2016 / 2019) XLOOKUP default choice · add if_not_found INDEX / MATCH robust in every version quick one-off, key in first column? VLOOKUP … , FALSE acceptable, but lock the range Thresholds / bands (0–50–100…)? 365: XLOOKUP(…, "n/a", -1) · older: VLOOKUP(…, TRUE) legacy version needs the thresholds sorted ascending Need the sheet name to vary? INDIRECT works but is volatile — prefer one combined Table
Start at the top. Solid boxes are recommendations; the dashed VLOOKUP box is "fine if you must".

Function A–Z

Every function with its own entry in this handbook. Click to jump.