Google Sheets QUERY function: the complete guide for analysts
QUERY is the most powerful function in Google Sheets. nothing else comes close. with one formula you can filter, group, sort, pivot, and join your data using SQL-like syntax, returning a fully dynamic result that updates as your source data changes.
most Sheets users never touch QUERY. they stack VLOOKUP, FILTER, SUMIF, and pivot tables to do what one QUERY formula could handle in a single line. once you learn QUERY, your formulas get shorter, your dashboards get faster, and you stop creating helper columns just to feed downstream lookups.
this guide walks through QUERY from zero. we use a 1,000-row sales tracker (Order ID, Date, Salesperson, Region, Product, Units, Revenue) so every example mirrors the kind of analysis a solopreneur or analyst actually does. by the end you will know the eight QUERY clauses and the ten patterns that cover most reporting work.
Google Sheets QUERY is a function that runs SQL-like queries against a range of cells. Syntax:
=QUERY(data, query, [headers]). The query string uses clauses: SELECT (which columns), WHERE (filter rows), GROUP BY (aggregate), PIVOT (create matrix), ORDER BY (sort), LIMIT (cap rows), LABEL (rename headers), FORMAT (number format). One formula replaces stacks of FILTER, SUMIF, COUNTIF, and pivot tables with a single dynamic result that updates as source data changes.
why QUERY is worth learning
every analyst who uses Sheets daily reaches a moment when basic functions stop scaling. you build a dashboard with FILTER for filtering, SUMIF for aggregating, and a pivot table for the cross-tab. the dashboard works but is slow, fragile, and expensive to update.
QUERY collapses all that into one function. five reasons it pays back the learning curve fast:
- single source of truth: one formula returns the whole result, not stitched-together pieces.
- dynamic: results recalc instantly when source data changes, no manual refresh.
- readable: SQL-like syntax is easier to understand than nested formulas.
- fast: QUERY is implemented in Sheets’ core engine, faster than equivalent FILTER+SUMIF stacks on large data.
- portable: anyone who knows SQL can read and edit your QUERY without learning Sheets internals.
QUERY is the Sheets equivalent of Power Query in Excel for ad-hoc analysis. for stored, refreshable transformations on Sheets data, see our Power Query Excel tutorial 2026 for the cross-platform comparison.
QUERY syntax overview
=QUERY(data, query, [headers])
- data: the range to query, like
Sales!A1:G1001or a named range. - query: a string holding the SQL-like query. always wrapped in quotes.
- headers (optional): how many rows are header rows. usually 1.
a minimal example: return all orders from the EMEA region.
=QUERY(Sales!A1:G1001, "SELECT * WHERE D = 'EMEA'", 1)
note the column reference syntax. inside QUERY, columns are referenced by letter (A, B, C, …, G), not by header name. that catches every beginner. the only exception is when QUERY is wrapped around a more complex source — then column names are quoted with backticks.
the eight QUERY clauses
QUERY supports eight clauses. they always appear in this order:
| clause | role | example |
|---|---|---|
| SELECT | which columns to return | SELECT A, B, sum(G) |
| WHERE | filter rows | WHERE D = ‘EMEA’ |
| GROUP BY | aggregate by columns | GROUP BY A, B |
| PIVOT | turn a column into headers | PIVOT C |
| ORDER BY | sort rows | ORDER BY G DESC |
| LIMIT | cap row count | LIMIT 10 |
| LABEL | rename output headers | LABEL sum(G) ‘Total Revenue’ |
| FORMAT | number format | FORMAT G ‘$#,##0.00’ |
memorize the order. QUERY rejects clauses out of order with a #VALUE! error.
the ten QUERY patterns you will use forever
pattern 1: select specific columns
problem: from the sales tracker, return only Salesperson, Region, and Revenue.
=QUERY(Sales!A1:G1001, "SELECT C, D, G", 1)
cleaner than building a manual extract. updates as source data grows.
pattern 2: filter with WHERE
problem: return all orders for Sarah Lee.
=QUERY(Sales!A1:G1001, "SELECT * WHERE C = 'Sarah Lee'", 1)
string values use single quotes inside the query string. if the formula itself uses double quotes for the query, single quotes are safe inside.
pattern 3: filter with multiple conditions
problem: orders from Sarah Lee in the APAC region with revenue above $1,000.
=QUERY(Sales!A1:G1001, "SELECT * WHERE C = 'Sarah Lee' AND D = 'APAC' AND G > 1000", 1)
AND, OR, NOT all work. parentheses to control precedence: WHERE (D = 'APAC' OR D = 'EMEA') AND G > 1000.
pattern 4: aggregate with GROUP BY
problem: total revenue by region.
=QUERY(Sales!A1:G1001, "SELECT D, sum(G) GROUP BY D", 1)
aggregation functions: sum, avg, count, min, max. when you use any aggregate, every non-aggregated column must appear in GROUP BY.
pattern 5: aggregate with multiple groupings
problem: total revenue by region by product.
=QUERY(Sales!A1:G1001, "SELECT D, E, sum(G) GROUP BY D, E", 1)
results are sorted by the grouping columns. add ORDER BY for custom sort.
pattern 6: pivot to a matrix
problem: revenue with regions as rows and products as columns.
=QUERY(Sales!A1:G1001, "SELECT D, sum(G) GROUP BY D PIVOT E", 1)
PIVOT takes the values in the named column and turns them into output columns. the result reads like a Pivot Table but is a live formula.
[SCREENSHOT: Sheets cell with the QUERY formula above and a matrix output showing regions on the left, product names across the top, revenue values in the cells]
pattern 7: top-N with ORDER BY and LIMIT
problem: top 10 customers by total revenue.
=QUERY(Sales!A1:G1001, "SELECT C, sum(G) GROUP BY C ORDER BY sum(G) DESC LIMIT 10", 1)
ORDER BY can be ASC (default) or DESC. LIMIT caps the result row count.
pattern 8: date filtering
problem: orders in April 2026.
=QUERY(Sales!A1:G1001, "SELECT * WHERE B >= date '2026-04-01' AND B <= date '2026-04-30'", 1)
dates inside QUERY use the date 'YYYY-MM-DD' literal. dynamic dates use & concatenation:
=QUERY(Sales!A1:G1001, "SELECT * WHERE B >= date '"&TEXT(H1,"yyyy-mm-dd")&"'", 1)
this is where QUERY syntax gets tricky. the inner string concatenation can fight escape rules. the trick: build the query string in a separate cell, then reference it.
pattern 9: rename output columns with LABEL
problem: an aggregated query has ugly headers like “sum revenue”.
=QUERY(Sales!A1:G1001, "SELECT D, sum(G) GROUP BY D LABEL sum(G) 'Total Revenue', D 'Region'", 1)
LABEL takes pairs of column expressions and quoted display names. always cleaner than wrapping QUERY in a header-replacement formula.
pattern 10: format numbers in output
problem: revenue should display as currency.
=QUERY(Sales!A1:G1001, "SELECT D, sum(G) GROUP BY D FORMAT sum(G) '$#,##0.00'", 1)
FORMAT takes the same number format codes you use in cell formatting. pair LABEL and FORMAT to ship dashboard-ready output.
numbered walkthrough: build your first QUERY
- open your sales tracker. confirm headers are in row 1, data runs A2:G1001.
- click into an empty cell, say I1.
- type
=QUERY(. - for the data argument, drag-select A1:G1001 (or type
A1:G1001if the data is on the same sheet). - type a comma, then a quote.
- type
SELECT D, sum(G) GROUP BY D. - close the quote, type a comma, then
1(for headers). - close the parenthesis.
- press Enter.
result: a table with two columns — Region and the sum of Revenue per region.
[SCREENSHOT: Sheets cell I1 with the formula visible in the formula bar and a 2-column result spilling into I1:J5]
now improve the output:
- wrap with LABEL:
... GROUP BY D LABEL sum(G) 'Total Revenue'. - add ORDER BY:
... ORDER BY sum(G) DESC. - format:
... FORMAT sum(G) '$#,##0.00'.
final formula:
=QUERY(A1:G1001, "SELECT D, sum(G) GROUP BY D ORDER BY sum(G) DESC LABEL sum(G) 'Total Revenue', D 'Region' FORMAT sum(G) '$#,##0.00'", 1)
QUERY vs FILTER vs Pivot Table
three Sheets tools answer similar questions. when do you use each?
| tool | best for | live updating | sort/group |
|---|---|---|---|
| QUERY | combined filter + aggregate + pivot in one formula | yes | full SQL-like |
| FILTER | simple row filtering only | yes | needs SORT wrapper |
| Pivot Table | drag-and-drop summary, multiple slicers | yes (manual refresh on some changes) | full GUI |
| SUMIF / COUNTIF | one aggregated value at a time | yes | none, returns scalar |
rules of thumb:
– one cell, one filtered list → FILTER
– one cell, one aggregated value → SUMIF
– one cell, full table with grouping or pivot → QUERY
– interactive exploration with drag-and-drop → Pivot Table
– everything else → QUERY first, then drop down to simpler functions only when QUERY is overkill
QUERY with column names instead of letters
QUERY normally references columns by letter (A, B, C). when wrapping QUERY around an IMPORTRANGE result or an array literal, column letters do not exist. use Col1, Col2, Col3 instead.
=QUERY(IMPORTRANGE("URL", "Sheet1!A1:G1001"), "SELECT Col4, sum(Col7) GROUP BY Col4", 1)
Col1 is the first column of whatever IMPORTRANGE returns. for the IMPORTRANGE flow specifically, see our IMPORTRANGE Google Sheets tutorial for the auth and refresh patterns.
handling errors and edge cases
#VALUE! — query syntax error
most common cause: clauses out of order, or a typo in a function name. paste the query string into a separate cell to read it without escape characters and find the typo.
#N/A — empty result
sometimes the query is correct but returns nothing because the filter matched no rows. wrap with IFERROR for cleaner output:
=IFERROR(QUERY(...), "No matching rows")
#REF! — circular reference
if the QUERY result spills into cells that already contain data, it errors out. clear the destination range before running the query.
unexpected aggregation result
if sum(G) returns 0 when you expected a real number, the column is probably stored as text, not number. check with =ISNUMBER(G2). fix by reformatting the column or running VALUE() on the source.
QUERY with dynamic inputs from cells
embedding cell values in the query string is the most common real-world pattern. say cell H1 holds a region name typed by the user.
=QUERY(A1:G1001, "SELECT * WHERE D = '"&H1&"'", 1)
read carefully:
– outer "..." is the query string
– '"&H1&"' injects the value of H1, wrapped in single quotes for SQL syntax
– if H1 is “APAC”, the assembled query becomes: SELECT * WHERE D = 'APAC'
for numeric values, drop the single quotes:
=QUERY(A1:G1001, "SELECT * WHERE G > "&H2, 1)
for dates:
=QUERY(A1:G1001, "SELECT * WHERE B >= date '"&TEXT(H3,"yyyy-mm-dd")&"'", 1)
build complex queries piece by piece in helper cells, then concatenate. that single trick fixes 90% of QUERY syntax pain.
related tutorials on DRAC
- Google Sheets ARRAYFORMULA complete walkthrough — the other Sheets superpower for vectorizing formulas
- IMPORTRANGE Google Sheets tutorial — pull data from another file before querying it
- Excel INDEX/MATCH tutorial: better than VLOOKUP — the Excel cousin for cross-tool comparisons
- linear regression Google Sheets no-code — feed QUERY results into regression for forecasting
conclusion: QUERY is the one Sheets function worth mastering deeply
most Sheets users learn ten functions and stop. learning QUERY thoroughly replaces five of those ten and makes your work faster, more readable, and more flexible.
start with the simple SELECT-WHERE pattern (pattern 2) for filtering. once that is muscle memory, move to GROUP BY (pattern 4) for aggregation, and then PIVOT (pattern 6) for cross-tabs. by the third week of consistent use, you will reach for QUERY before opening a new pivot table.
QUERY is also a stepping stone to real SQL. once you are fluent, transitioning to BigQuery, Supabase, or Postgres becomes the difference between learning new syntax (small) and learning a new mental model (which you already have).
next action: open a real spreadsheet you maintain. find the most complex stack of FILTER, SUMIF, or pivot table you have built. rewrite it as a single QUERY. you will end up with one formula where you used to have a dozen.