Skip to content

DB.QUERY

=DB.QUERY runs a SQL query against the sources you named with =DB.SOURCE and spills the result into the grid. DuckDB runs inside the browser, so the query reads each source directly — nothing round-trips through a server.

=DB.QUERY(sql, source1, [source2], …)

The query comes first; every source after it is a cell created with =DB.SOURCE. The function is variadic — pass as many source cells as the query references.

ParameterTypeDescription
sqltextA SQL query. Reference each source by the alias you gave it in =DB.SOURCE. Required and non-empty.
source1, source2, …cellOne cell per source, each holding the result of =DB.SOURCE. Reference a source cell directly — an inline =DB.SOURCE(…) written inside =DB.QUERY is rejected.

A query that needs no source is allowed: =DB.QUERY("SELECT 1") evaluates constant SQL with no source cells.

Each source cell binds a SQL alias to a resolved source. Use that alias anywhere a table name would go — in FROM, in a JOIN, in a subquery:

SELECT region_id, SUM(revenue) AS total
FROM sales -- "sales" is the alias from =DB.SOURCE
GROUP BY region_id

The column names in the spilled header come from DuckDB’s result schema, so a SELECT … AS total shows total in the header.

DB.QUERY spills an (N+1) × C array: C is the number of columns, N the number of result rows.

  • Row 1 is the column-name header, taken from the query’s result schema (SQL aliases are honoured).
  • Rows 2…N+1 are the data.
  • A query that matches zero rows spills the header row alone.

To work with the data without the header, wrap the spill with Excel’s DROP: =DROP(B1#, 1).

LimitBehaviour
60-second timeoutA query still running after 60 seconds is cancelled and the cell shows #N/AQuery timed out (60 s) — try a smaller filter.
1,000,000-row capA result larger than 1,000,000 rows is refused with #NUM! rather than spilled. Reduce it with LIMIT or aggregation.
RecalculationIf the cell recalculates (an input changed) while a query is in flight, the running query is cancelled and the new one takes over.
ConditionCell errorMessage
sql is empty or not text#VALUE!SQL must be a non-empty string
A source host can’t be reached (404, 403, timeout, DNS)#N/ASource not reachable: alias
The query ran longer than 60 seconds#N/AQuery timed out (60 s) — try a smaller filter
The result exceeds 1,000,000 rows#NUM!Result has N rows; reduce with LIMIT or aggregation
Files unioned in one source have mismatched columns#VALUE!Schemas differ across files in source array
A file can’t be parsed as the expected format#VALUE!Cannot parse source: …
The SQL is invalid (unknown column, syntax error, unknown alias)#VALUE!SQL: …

Top selling regions from the public samples corpus. A1 names the source; B1 queries it:

A1: =DB.SOURCE("https://dev-fdb.sumpalabs.com/samples/sales.parquet", "sales")
B1: =DB.QUERY("SELECT region_id, SUM(revenue) AS total FROM sales GROUP BY region_id ORDER BY total DESC", A1)

B1 spills a header row region_id | total followed by one row per region, sorted by total revenue.

  • =DB.SOURCE — name a source before you query it.
  • =DB.SCHEMA — list a source’s columns and types.
  • Examples — full worked queries, including JOINs across sources.