The 4 QUERY patterns FP&A analysts use most often are WHERE, ORDER BY, SELECT DISTINCT, and GROUP BY. Excel handles 3 of them cleanly and one with a workaround. Here's what each replacement looks like when the data is real.
QUERY Syntax vs. Excel Equivalent: Quick Reference
| QUERY clause | Excel equivalent | Notes |
|---|---|---|
SELECT A, B, C | CHOOSE({1,2,3}, A:A, B:B, C:C) | Wrap around FILTER for column selection |
WHERE x = 'val' | FILTER(range, condition) | Use * for AND, + for OR |
ORDER BY D DESC | SORTBY(range, sort_col, -1) | Sort key can be outside output range |
SELECT DISTINCT A | UNIQUE(A:A) | Composes cleanly with FILTER and SORT |
GROUP BY A, SUM(B) | UNIQUE() + SUMIFS(…, A2#) | Spill reference is the key |
LIMIT n | TAKE(result, n) | Available Excel 365 (2022+) |
According to Google's Visualization API Query Language documentation, QUERY uses "a query language similar to SQL" with 12 supported clauses. Excel's dynamic array stack covers 8 of them reliably as of July 2026.
WHERE → FILTER (With Column Selection)
FILTER returns entire rows. QUERY lets you specify columns inline - SELECT B, D, F WHERE.... FILTER doesn't.
The fix is CHOOSE with an array constant to pick specific columns before or after FILTER evaluates. For a revenue detail pull filtering ARR contracts above $500K:
=FILTER(
CHOOSE({1,2,3},
'Revenue Detail'!B2:B5000, -- Account name
'Revenue Detail'!D2:D5000, -- Revenue type
'Revenue Detail'!F2:F5000), -- Contract value
('Revenue Detail'!C2:C5000 = "ARR") *
('Revenue Detail'!F2:F5000 >= 500000)
)
The * operator is AND logic. + is OR. For multi-condition filters reaching across tabs into your assumptions sheet:
=FILTER(
CHOOSE({1,2,3,4},
'GL Detail'!A2:A10000,
'GL Detail'!C2:C10000,
'GL Detail'!D2:D10000,
'GL Detail'!F2:F10000),
('GL Detail'!B2:B10000 = Assumptions!$B$4) *
('GL Detail'!E2:E10000 >= Assumptions!$C$3) *
('GL Detail'!E2:E10000 <= Assumptions!$D$3)
)
Entity filter anchored to Assumptions!$B$4, date range from $C$3:$D$3. This is the formula structure behind a quarterly actuals pull for a board pack - the kind where the CFO asks you to re-run it for a different entity 20 minutes before the meeting.
ORDER BY → SORTBY
SORT handles single-column ascending/descending. SORTBY is what you need when the sort key isn't in your output, which happens constantly in contribution margin tables, deal rankings, and SKU performance summaries.
Sorting active SKUs by gross margin dollars descending, displaying only name and revenue:
=SORTBY(
FILTER(
CHOOSE({1,2}, 'SKU Detail'!A2:A500, 'SKU Detail'!C2:C500),
'SKU Detail'!E2:E500 = "Active"
),
FILTER('SKU Detail'!D2:D500, 'SKU Detail'!E2:E500 = "Active"),
-1
)
The sort array must match the output array row-for-row. The inner FILTER on the sort key has to use the identical condition as the outer one, or you get a #VALUE! error. That's the main trip wire here.
SELECT DISTINCT → UNIQUE
QUERY's SELECT DISTINCT is UNIQUE in Excel, and it composes more cleanly.
A dynamic dimension list for a cost center variance report, sorted alphabetically:
=SORT(UNIQUE('GL Detail'!D2:D10000))
Two functions. In QUERY you'd write SELECT DISTINCT D ORDER BY D and still need to handle the header row separately.
Where UNIQUE earns its place in a real model: generating row labels for a summary table that auto-expands as new cost centers appear in the GL. No pivot table refresh, no hardcoded list to maintain, no broken reference when finance adds a new department mid-year.
GROUP BY - The One That Requires a Workaround
This is where the QUERY analogy breaks. QUERY's GROUP BY with aggregation is a single formula. Excel has no direct equivalent - you need 2 formulas working together.
The pattern: UNIQUE generates the dimension list, SUMIFS with a spill reference does the aggregation.
In column A (dimension labels, spills down automatically):
=SORT(UNIQUE('GL Detail'!B2:B3000))
In column B (aggregated EBITDA, one formula that covers the entire column):
=SUMIFS('GL Detail'!F2:F3000, 'GL Detail'!B2:B3000, A2#)
The A2# spill reference is the key. SUMIFS evaluates against every value in the spill range and returns a matching array, one result per business unit. This replicates SELECT B, SUM(F) GROUP BY B ORDER BY B in 2 formulas instead of 1.
For a multi-condition group-by - say, EBITDA by entity and cost category, pulling actuals from a P&L tab with 5,000+ rows:
=SUMIFS(
'P&L'!D2:D5000, -- Values to sum
'P&L'!B2:B5000, Dimensions!A2#, -- Entity (spill ref)
'P&L'!C2:C5000, ">=" & Assumptions!$C$3, -- Period start
'P&L'!C2:C5000, "<=" & Assumptions!$D$3, -- Period end
'P&L'!E2:E5000, "Operating Expense" -- Line item type
)
This is more verbose than QUERY but more auditable. Every condition is visible. Your reviewer can trace each argument without running the formula in a test environment.
The Version Constraint Nobody Mentions
FILTER, SORT, SORTBY, and UNIQUE require Excel 365 or Excel 2021. They're not available in Excel 2019 or earlier. According to Microsoft's Excel documentation, these dynamic array functions were introduced in Excel 365 starting in 2019, with Excel 2021 perpetual licenses adding them in October of that year.
If your model needs to open on legacy Excel - common in banking where IT policy can run 3+ years behind current releases - every dynamic array formula breaks. Your FILTER becomes #NAME?. You're back to SUMIFS for aggregation, INDEX/MATCH for filtering, and pivot tables for anything GROUP BY-adjacent.
That's a real constraint in bank syndicate DCF work where the counterparty opens your file in a controlled environment running Excel 2016 or 2019. QUERY has the same portability problem in Sheets (it can't reach external files), but at least it works consistently within the platform.
What QUERY Does That Excel Still Can't Match Cleanly
Two patterns don't translate well:
Inline calculated columns. QUERY allows SELECT A, B*C AS gross_profit WHERE... and returns the calculated column in the output. FILTER returns stored values only. You need a helper column, or BYROW/LAMBDA, which works in Excel 365 but isn't standard practice in most FP&A models as of mid-2026.
The PIVOT clause. QUERY can transpose row values into column headers dynamically. There's no Excel equivalent short of VBA or Power Query. If your QUERY was doing cross-tab pivoting inline, that's the use case that genuinely has no clean replacement in standard Excel formulas.
Rebuilding QUERY Formulas at Scale
If you're migrating a Sheets model with 15-20 QUERY formulas to Excel, the translation work is mechanical but tedious. Each formula has to be decomposed into component operations, column selections rewritten with CHOOSE arrays, and GROUP BY logic split into UNIQUE + SUMIFS pairs.
ModelMonkey handles this inside the spreadsheet - describe the QUERY you had in Sheets, and it writes the FILTER/SORT/UNIQUE equivalent matched to your actual data layout and tab structure. For a full model migration that would take a senior analyst 2-3 hours to do cleanly by hand, it runs in minutes. Try ModelMonkey free for 14 days - it works in both Google Sheets and Excel.