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.
Signature
Section titled “Signature”=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.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
sql | text | A SQL query. Reference each source by the alias you gave it in =DB.SOURCE. Required and non-empty. |
source1, source2, … | cell | One 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.
Alias resolution
Section titled “Alias resolution”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 totalFROM sales -- "sales" is the alias from =DB.SOURCEGROUP BY region_idThe column names in the spilled header come from DuckDB’s result schema, so a SELECT … AS total shows total in the header.
Return shape
Section titled “Return shape”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).
Limits
Section titled “Limits”| Limit | Behaviour |
|---|---|
| 60-second timeout | A query still running after 60 seconds is cancelled and the cell shows #N/A — Query timed out (60 s) — try a smaller filter. |
| 1,000,000-row cap | A result larger than 1,000,000 rows is refused with #NUM! rather than spilled. Reduce it with LIMIT or aggregation. |
| Recalculation | If the cell recalculates (an input changed) while a query is in flight, the running query is cancelled and the new one takes over. |
Errors
Section titled “Errors”| Condition | Cell error | Message |
|---|---|---|
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/A | Source not reachable: alias |
| The query ran longer than 60 seconds | #N/A | Query 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: … |
Example
Section titled “Example”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.
See also
Section titled “See also”=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.