Google Sheets QUERY Function: Tips and Tricks (2026)
HomeGoogle SheetsQUERY Tips & Tricks
Google SheetsHow-To Post⏱ 14 min read

Google Sheets QUERY Function: Tips and Tricks (2026)

QUERY is Google Sheets' secret weapon — a mini SQL engine that filters, groups, sorts, and pivots data with a single formula. Once you're comfortable with it, you'll stop reaching for SUMIFS and INDEX-MATCH combos entirely. This guide covers the syntax, the tricky bits (dates!), and the patterns that make QUERY genuinely powerful.

The syntax in 60 seconds

=QUERY(data, "SELECT clauses", [headers])

Three arguments:

  • data — the range to query (e.g., A1:D1000, or a named range, or another formula's output)
  • query string — SQL-like clauses in quotes: SELECT, WHERE, GROUP BY, ORDER BY, LIMIT, LABEL, FORMAT, PIVOT
  • headers — number of header rows in your data (usually 1)

The simplest possible QUERY

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

Returns everything. Not useful on its own, but shows the pattern.

Column references

QUERY uses column letters that correspond to positions within your source range, not the sheet's actual columns.

Column letters are relative to the range, not the sheet

If your data range is C1:F100, column A in the query means column C in the sheet. Off-by-one column bugs are the #1 source of QUERY frustration.

SELECT tricks

Multiple columns in a specific order

=QUERY(SalesData, "SELECT B, A, D", 1)

Order matters. Output columns appear in the order you list them, not the source order.

Skip columns you don't need

=QUERY(SalesData, "SELECT B, D", 1)

Only Region and Revenue. Skips Date and Product entirely.

Aggregations in SELECT

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

=QUERY(SalesData, "SELECT SUM(D)", 1)

Total revenue across everything.

Arithmetic in SELECT

=QUERY(SalesData, "SELECT A, D, D * 0.15", 1)

Adds a computed column: 15% of Revenue. Useful for on-the-fly calculations without extra columns in your source data.

String concatenation with column references

=QUERY(SalesData, "SELECT A, B, C", 1)

QUERY has no CONCAT function in its dialect. For text combinations, do it before or after QUERY — with helper columns or by wrapping in ARRAYFORMULA.

WHERE filtering

Basic equality

=QUERY(SalesData, "SELECT * WHERE B = 'West'", 1)

Text values need single quotes. Numbers don't.

Numeric comparisons

=QUERY(SalesData, "SELECT * WHERE D > 1000", 1)

Available operators: =, !=, <>, <, >, <=, >=.

Multiple conditions

=QUERY(SalesData, "SELECT * WHERE B = 'West' AND D > 1000", 1)
=QUERY(SalesData, "SELECT * WHERE B = 'West' OR B = 'East'", 1)

IN for multiple values

Instead of chaining ORs, use MATCHES with a regex:

=QUERY(SalesData, "SELECT * WHERE B MATCHES 'West|East|Central'", 1)

QUERY doesn't have SQL's IN keyword, but MATCHES with a regex covers the same ground and is often cleaner.

Pattern matching with LIKE

=QUERY(SalesData, "SELECT * WHERE C LIKE '%widget%'", 1)

% is the wildcard for zero-or-more characters. LIKE is case-sensitive.

Contains vs starts with vs ends with

=QUERY(SalesData, "SELECT * WHERE C CONTAINS 'widget'", 1)
=QUERY(SalesData, "SELECT * WHERE C STARTS WITH 'wid'", 1)
=QUERY(SalesData, "SELECT * WHERE C ENDS WITH 'A'", 1)

Not null and null

=QUERY(SalesData, "SELECT * WHERE D IS NOT NULL", 1)

Filters out rows where Revenue is blank. Useful for cleaning imported data.

Case sensitivity note

Most QUERY operators are case-sensitive. "west" won't match "West". Wrap in LOWER for case-insensitive comparison:

=QUERY(SalesData, "SELECT * WHERE LOWER(B) = 'west'", 1)

GROUP BY aggregation

Total by category

=QUERY(SalesData, "SELECT B, SUM(D) GROUP BY B", 1)

Every unique region and its total revenue. This is the equivalent of a small pivot table in one formula.

Multiple grouping columns

=QUERY(SalesData, "SELECT B, C, SUM(D) GROUP BY B, C", 1)

Every unique Region + Product combination and its total. All non-aggregated SELECT columns must appear in GROUP BY.

Multiple aggregations

=QUERY(SalesData, "SELECT B, SUM(D), AVG(D), COUNT(D) GROUP BY B", 1)

Region, total, average, and count in one output.

Filtered aggregations

=QUERY(SalesData, "SELECT B, SUM(D) WHERE D > 500 GROUP BY B", 1)

Only counts revenue over 500. WHERE runs before GROUP BY.

The "all SELECT columns in GROUP BY" rule

Any non-aggregated column in SELECT must appear in GROUP BY. Break this rule and QUERY returns "Column X should be added to group by clause, or an aggregation function should be used". Beginners hit this constantly — remember: aggregate it, group by it, or don't select it.

ORDER BY

=QUERY(SalesData, "SELECT B, SUM(D) GROUP BY B ORDER BY SUM(D) DESC", 1)

Highest total revenue first. ASC (or omitted) for ascending; DESC for descending.

Ordering by multiple columns

=QUERY(SalesData, "SELECT B, C, SUM(D) GROUP BY B, C ORDER BY B ASC, SUM(D) DESC", 1)

Region alphabetical, then revenue high-to-low within each region.

LIMIT and OFFSET

Top N results

=QUERY(SalesData, "SELECT B, SUM(D) GROUP BY B ORDER BY SUM(D) DESC LIMIT 5", 1)

Top 5 regions by revenue.

Pagination with OFFSET

=QUERY(SalesData, "SELECT * ORDER BY D DESC LIMIT 10 OFFSET 10", 1)

Rows 11-20 in the sorted result. Perfect for building paginated views.

LABEL and FORMAT

Rename output columns

=QUERY(SalesData, "SELECT B, SUM(D) GROUP BY B LABEL SUM(D) 'Total Revenue'", 1)

Without LABEL, QUERY names the aggregate column "sum D" — ugly and unhelpful. LABEL cleans it up.

Multiple labels

=QUERY(SalesData, "SELECT B, SUM(D), COUNT(D) GROUP BY B LABEL SUM(D) 'Revenue', COUNT(D) 'Deals'", 1)

Hide the header row

=QUERY(SalesData, "SELECT B, SUM(D) GROUP BY B LABEL B '', SUM(D) ''", 1)

Empty labels remove the header entirely. Useful when embedding QUERY output into a custom-styled report.

Format numbers inline

=QUERY(SalesData, "SELECT B, SUM(D) GROUP BY B FORMAT SUM(D) '$#,##0.00'", 1)

QUERY formats the output values without touching the source. Same syntax as Excel's number formats.

Dates in QUERY (the tricky part)

Dates confuse people more than any other QUERY topic. The rules:

Literal date in WHERE

=QUERY(SalesData, "SELECT * WHERE A >= date '2026-01-01'", 1)

Format: date 'yyyy-mm-dd'. The lowercase date keyword is required. Single quotes required. Any other format fails.

Date range

=QUERY(SalesData, "SELECT * WHERE A >= date '2026-01-01' AND A < date '2026-04-01'", 1)

Filters to Q1 2026. Using < for the end date (not <=) avoids including part of the next quarter.

Date from a cell reference

=QUERY(SalesData, "SELECT * WHERE A >= date '"&TEXT(B1,"yyyy-mm-dd")&"'", 1)

TEXT with yyyy-mm-dd converts B1 into the exact format QUERY expects, then concatenation drops it into the string. Note the escaped single quotes.

DateTime literals

=QUERY(SalesData, "SELECT * WHERE A > datetime '2026-01-01 09:00:00'", 1)

Use datetime instead of date when time-of-day matters.

Aggregating by date parts

=QUERY(SalesData, "SELECT YEAR(A), MONTH(A), SUM(D) GROUP BY YEAR(A), MONTH(A)", 1)

Total revenue by year-month. Available date extractors: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND, QUARTER, DAYOFWEEK.

MONTH returns 0-11 in QUERY, not 1-12

QUERY's MONTH function is 0-indexed — January is 0, December is 11. Weird but true. Add 1 in a wrapper formula if you want conventional numbering, or use FORMAT to display month names instead.

Cell references in queries

Dropdown-driven filter

=QUERY(SalesData, "SELECT * WHERE B = '"&E1&"'", 1)

E1 holds a dropdown value (via data validation). Change the dropdown → QUERY re-runs with the new value.

Number reference (no quotes)

=QUERY(SalesData, "SELECT * WHERE D > "&E1, 1)

Numeric values don't need single quotes in the query string.

"All" option workaround

SUMIFS/COUNTIFS have wildcards; QUERY doesn't in the same way. Pattern for an "All" option:

=QUERY(SalesData, "SELECT * "&IF(E1="All", "", "WHERE B = '"&E1&"'"), 1)

When dropdown is "All", the WHERE clause is empty. Otherwise it filters. Concatenation lets you construct the query string conditionally.

Multiple filter cells

=QUERY(SalesData, "SELECT * WHERE B = '"&E1&"' AND C = '"&E2&"' AND D > "&E3, 1)

As many cell-driven conditions as you need. For anything past 3-4, put the query string in a helper cell and reference it — easier to read and debug.

The PIVOT clause

QUERY can produce cross-tabulations natively — no pivot table needed.

=QUERY(SalesData, "SELECT B, SUM(D) GROUP BY B PIVOT C", 1)

Regions become rows. Products become columns. SUM(D) fills the cells. Beautifully compact.

When PIVOT is right

  • The pivot needs to update instantly as data changes (real pivot tables need refresh)
  • Structure is simple: one row dimension, one column dimension, one metric
  • You want the result to feed further formulas

When to use a real pivot table instead

  • Multiple row or column dimensions
  • Multiple metrics with different aggregations
  • Interactive filtering with slicers
  • Formatting flexibility (subtotals, grand totals, styling)

QUERY + IMPORTRANGE — the classic combo

Pull data from another spreadsheet and query it in one formula.

=QUERY(IMPORTRANGE("1abc...xyz", "Data!A:D"), "SELECT Col2, SUM(Col4) WHERE Col2 = 'West' GROUP BY Col2", 1)

The Col1, Col2, Col3 rule

When the data source is an IMPORTRANGE, QUERY doesn't see A, B, C — it sees Col1, Col2, Col3. This trips up almost everyone the first time.

The classic IMPORTRANGE + QUERY gotchas
  • Use Col1/Col2/Col3, not A/B/C
  • Header row is often included as data if the source sheet is different — set the header argument accordingly
  • Both spreadsheets must have granted permission (first use prompts for it)
  • IMPORTRANGE refreshes on a delay — if the source updated seconds ago, QUERY may still see old data

Filtering an import to reduce load

=QUERY(IMPORTRANGE("...", "Data!A:D"), "SELECT * WHERE Col1 >= date '2026-01-01' LIMIT 1000", 1)

Pull only what you need. Big IMPORTRANGE queries slow the whole spreadsheet — filtering aggressively at the QUERY level helps.

Killer tricks

1. Running totals

QUERY itself doesn't do running totals, but combine with ARRAYFORMULA and MMULT for a cumulative column. Simpler approach — sort by date, then reference the QUERY output:

=QUERY(SalesData, "SELECT A, SUM(D) GROUP BY A ORDER BY A", 1)

In the adjacent column: =SUMIF(A2:A, "<="&A2, B2:B) for the running total.

2. Top N per category

QUERY doesn't have a native "top N per group" clause. Workaround using LIMIT with a filter:

=QUERY(SalesData, "SELECT * WHERE B = 'West' ORDER BY D DESC LIMIT 3", 1)

Repeat for each region, or wrap in a more complex ARRAYFORMULA if you need it in one shot.

3. Skip blanks in source data

=QUERY(SalesData, "SELECT * WHERE A IS NOT NULL", 1)

Filters out empty rows without affecting your source data.

4. Count uniques per category

=QUERY(SalesData, "SELECT B, COUNT(A) GROUP BY B", 1)

QUERY's COUNT works on any column and counts non-blank values. For distinct counts, wrap in COUNTUNIQUE outside:

=COUNTUNIQUE(FILTER(A2:A, B2:B = "West"))

5. Sum only positive values

=QUERY(SalesData, "SELECT B, SUM(D) WHERE D > 0 GROUP BY B", 1)

Filters at the row level before aggregating.

6. Extract distinct combinations

=QUERY(SalesData, "SELECT B, C GROUP BY B, C", 1)

GROUP BY without an aggregation returns distinct combinations. Faster than UNIQUE on multi-column ranges.

7. Show only certain rows from an existing query

QUERY can query another QUERY's output. Store the first in a helper range or wrap directly:

=QUERY(QUERY(SalesData, "SELECT B, SUM(D) GROUP BY B", 1), "SELECT * WHERE Col2 > 10000", 1)

Filters the aggregated results. Note the Col1/Col2 syntax for the inner query's output.

8. Add a total row

QUERY has no built-in total row. Add one with SUM below, or wrap in a range that includes a manual total. Ugly but works.

9. Query multiple sheets combined

=QUERY({Sheet1!A2:D; Sheet2!A2:D; Sheet3!A2:D}, "SELECT Col1, SUM(Col4) GROUP BY Col1", 0)

Curly-brace stacking combines ranges vertically. Header argument is 0 because no headers in the stacked data. Use Col1/Col2 syntax.

10. Wrap headers in a merged range

QUERY output can be styled by placing it below a manually-formatted header row. Set the header argument to 0 and LABEL your columns to blank strings — the query returns only data, and your styled headers stay untouched.

Common pitfalls

Mixing quote types

Query strings need double quotes on the outside. Text values inside the query need single quotes. Get them backwards and you get parse errors or unexpected behavior. Watch every quote character.

Concatenation escaping

When building queries with cell references, the syntax gets dense: "...WHERE B = '"&E1&"'...". That's double-quote, apostrophe, ampersand, cell-ref, ampersand, apostrophe, double-quote. Miss any of them and QUERY parses it wrong. Build in stages — start with a working literal query, then swap in the cell reference one piece at a time.

Case sensitivity in WHERE

QUERY's comparisons are case-sensitive by default. "West" doesn't match "west" or "WEST". Wrap in LOWER for case-insensitive matching.

Column limits differ between formats

Direct range queries use A, B, C. IMPORTRANGE queries use Col1, Col2. Array literal queries (curly braces) also use Col1, Col2. Choosing the wrong one is a common cause of "no column" errors.

QUERY doesn't support text concatenation

You can't do SELECT A + B for text. Use helper columns with CONCATENATE, or wrap the whole QUERY in a formula that adds the concatenation after.

Empty result behavior

When a query matches nothing, it returns #N/A or "No data" depending on how it's structured. Wrap in IFERROR for user-friendly fallbacks: =IFERROR(QUERY(...), "No results").

Data type mismatches

QUERY infers each column's type from the data. If a numeric column has one text value (say, "N/A"), QUERY may treat the whole column as text and comparisons fail. Clean your data or use VALUE() in a helper column.

Sheets Wizard

QUERY without memorizing the syntax

QUERY is powerful but its syntax rewards frequent use — the dates, the Col1 vs A, the quote escaping. Sheets Wizard generates the exact QUERY formula from plain-English intent ("sum revenue by region for Q1, sorted descending"), with proper syntax and formatting.

Install Sheets Wizard →

Frequently asked questions

What does the QUERY function do in Google Sheets?

Runs SQL-style commands against a range — filter, group, sort, aggregate — with one formula. Replaces multiple SUMIFS, COUNTIFS, and lookup formulas.

How do I filter dates in a QUERY?

Use the date keyword with yyyy-mm-dd format: WHERE A >= date '2026-01-01'. For cell references: WHERE A >= date '"&TEXT(B1,"yyyy-mm-dd")&"'.

How do I use a cell reference in a QUERY?

Concatenate the cell into the query string: =QUERY(data, "SELECT A WHERE B = '"&C1&"'", 1). Text values need single quotes; numbers don't.

Why does my QUERY return #VALUE with 'no column'?

Column letters don't match the range positions. If data starts at C1, column A in the query is the sheet's column C. IMPORTRANGE queries use Col1, Col2 instead of A, B.

Can QUERY do a pivot table?

Yes, with the PIVOT clause: SELECT A, SUM(D) GROUP BY A PIVOT B. A becomes rows, B becomes columns, SUM(D) fills the cells.