The QUERY function lets you filter, sort, group, and summarize data using a SQL-like language. It's more powerful than FILTER for complex conditions and more readable than stacking SUMIFS for grouped totals.

If you know even basic SQL, QUERY will feel immediately familiar. If you don't, this tutorial explains it from scratch.

---

The Syntax

Code
=QUERY(data, query_string, [headers])

| Argument | What it means | |----------|--------------| | data | The range to query — a table with a header row | | query_string | A SQL-like string that defines what to return | | headers | Optional. Number of header rows in your data. Usually 1. |

---

How Column References Work

In QUERY, columns are referenced by letter: Col1, Col2, etc. — or if your data starts at column A, just A, B, C.

If your data is in A1:D100:

  • Column A = A
  • Column B = B
  • Column C = C

If your data is in C1:F100 (not starting at A):

  • First column (C) = Col1
  • Second column (D) = Col2

It's simpler to start your tables at column A.

---

SELECT: Choose Which Columns to Return

Return all columns:

Code
=QUERY(A1:D100, "SELECT *")

Return only columns A and C:

Code
=QUERY(A1:D100, "SELECT A, C")

---

WHERE: Filter Rows

Return only rows where column C is "Done":

Code
=QUERY(A1:D100, "SELECT * WHERE C = 'Done'")

Text values use single quotes. Numbers and dates don't.

Return rows where column B is greater than 100:

Code
=QUERY(A1:D100, "SELECT * WHERE B > 100")

Multiple conditions with AND / OR:

Code
=QUERY(A1:D100, "SELECT * WHERE C = 'Done' AND B > 100")
Code
=QUERY(A1:D100, "SELECT * WHERE C = 'Overdue' OR C = 'Due Soon'")

---

Using Cell Values in Queries

The query string is a text string — you can't directly reference cells inside it. Use & to concatenate cell values into the query:

Code
=QUERY(A1:D100, "SELECT * WHERE C = '" & E1 & "'")

This queries for rows where column C matches whatever is in cell E1. The single quotes inside the string are required for text matches.

For numbers:

Code
=QUERY(A1:D100, "SELECT * WHERE B > " & F1)

---

ORDER BY: Sort Results

Sort by column B descending:

Code
=QUERY(A1:D100, "SELECT * ORDER BY B DESC")

Sort by column C ascending, then by B descending:

Code
=QUERY(A1:D100, "SELECT * ORDER BY C ASC, B DESC")

---

LIMIT: Return Only N Rows

Return the top 10 rows after sorting:

Code
=QUERY(A1:D100, "SELECT * ORDER BY B DESC LIMIT 10")

---

GROUP BY and Aggregate Functions

QUERY can group rows and calculate totals — similar to a pivot table but formula-based and dynamic.

Return total of column B grouped by column C (category):

Code
=QUERY(A1:D100, "SELECT C, SUM(B) GROUP BY C")

Available aggregate functions: SUM, COUNT, AVG, MAX, MIN.

Add a LABEL clause to rename the output column:

Code
=QUERY(A1:D100, "SELECT C, SUM(B) GROUP BY C LABEL SUM(B) 'Total'")

---

Filtering by Date

Dates in QUERY use the date 'YYYY-MM-DD' format:

Code
=QUERY(A1:D100, "SELECT * WHERE A >= date '2026-04-01'")

To use a dynamic date (like TODAY()):

Code
=QUERY(A1:D100, "SELECT * WHERE A >= date '" & TEXT(TODAY(),"yyyy-MM-dd") & "'")

---

Querying Data From Another Sheet

Code
=QUERY(Expenses!A:D, "SELECT * WHERE C = 'Software'")

The sheet name comes before !, then the range. If the sheet name has spaces, wrap it in single quotes: 'My Sheet'!A:D.

---

Common Errors

#VALUE! — parse error Usually a syntax issue in your query string. Check for:

  • Missing single quotes around text values
  • Column letters that don't match your data range
  • Typos in keywords (SELECT, WHERE, etc.)

Returns empty result when data exists Your WHERE condition doesn't match. Check case — QUERY text matching is case-insensitive by default, but check for extra spaces. Use TRIM() on the source data if needed.

#N/A — no data Your query returned zero rows. This isn't an error — it means the filter found nothing. Wrap in IFERROR if you want to suppress it.

Column reference is wrong If your data doesn't start in column A, use Col1, Col2, etc. instead of letter references.

---

QUERY vs FILTER

FILTER is simpler for basic row filtering and returns results dynamically. QUERY is better when you need:

  • Aggregation (SUM, COUNT, GROUP BY)
  • Multi-column sorting
  • Column selection (not returning all columns)
  • Complex conditions mixing AND/OR

For simple "show me rows where X is Y," FILTER is often easier to read.

---

What to Build on Top of This

Don't want to set this up yourself? Describe what you're trying to filter or summarize and it'll be configured for your sheet. Get it installed