Mastering the QUERY Function in Google Sheets
July 18th, 2026
QUERY runs a small SQL-like language directly inside a cell. Once you know the clauses, it replaces stacks of FILTER, SORT, and SUMIF formulas with a single readable statement — and it's one of the few Sheets functions that can filter, sort, group, and label a result set all at once.
The basic shape
=QUERY(data, query, [headers])
data— the range to query.query— a string written in Google Visualization API Query Language (a SQL subset).[headers]— optional number of header rows indata; QUERY uses this to know which row to skip and how to label output columns.
Columns are referenced by their spreadsheet letter relative to the range, not by header name — inside the query string, the first column of data is always A, regardless of which sheet column it actually lives in.
=QUERY(A1:D100, "SELECT A, C WHERE B = 'Active'", 1)
This returns columns A and C, only for rows where column B is Active, treating row 1 of the range as a header.
SELECT and WHERE
SELECT picks columns; WHERE filters rows. Both accept multiple conditions:
=QUERY(A1:E200, "SELECT A, B, E WHERE D > 100 AND C = 'EU'", 1)
String comparisons need single quotes; numbers don't. WHERE also supports CONTAINS, STARTS WITH, IS NULL, and pattern matching with MATCHES (regex):
=QUERY(A1:B200, "SELECT A WHERE B CONTAINS 'refund'", 1)
ORDER BY and LIMIT
Sort and cap results without touching the source data:
=QUERY(A1:C500, "SELECT A, B, C ORDER BY C DESC LIMIT 10", 1)
That's a top-10 report — sorted, trimmed, and separate from the raw data — in one formula, no helper columns.
GROUP BY and aggregates
QUERY can summarize, which is where it starts replacing pivot tables for simple cases. Any column in SELECT that isn't wrapped in an aggregate function must appear in GROUP BY:
=QUERY(A1:C500, "SELECT A, SUM(C) WHERE B = 'Sales' GROUP BY A ORDER BY SUM(C) DESC", 1)
This groups by column A, sums column C per group, restricts to rows where B is Sales, and sorts groups by total descending. COUNT, AVG, MIN, and MAX work the same way.
LABEL: renaming output columns
Aggregated columns come back with ugly default headers like sum C. LABEL fixes that:
=QUERY(A1:C500, "SELECT A, SUM(C) GROUP BY A LABEL SUM(C) 'Total Sales'", 1)
PIVOT: turning row values into columns
PIVOT reshapes data the way a pivot table would — one clause, no dragging fields into panes:
=QUERY(A1:C500, "SELECT A, SUM(C) GROUP BY A PIVOT B", 1)
If A is salesperson, B is month, and C is revenue, this produces one row per salesperson with one column per month, each cell holding that month's summed revenue.
Referencing another sheet or cell values
data can point at another sheet or another spreadsheet entirely (combined with IMPORTRANGE), and query conditions can pull from a cell instead of being hardcoded, using string concatenation:
=QUERY(Sheet2!A1:D, "SELECT A, B WHERE C = '"&E1&"'", 1)
That makes the query dynamic — change the value in E1 and the filter updates without editing the formula.
When to reach for QUERY
- You need filtering, sorting, and aggregation together, not just one of them.
- You want a live summary table that updates automatically as source data grows — unlike a static pivot table snapshot in some setups, QUERY recalculates like any formula.
- You're pulling from IMPORTRANGE or another sheet and want to shape the data on the way in, rather than importing everything and filtering after.
For a single filter or a single sort, FILTER or SORT alone is more readable. QUERY earns its complexity when you need several of these operations to happen together.