Skip to content

Limitations and best practices

This page summarizes supported features, limitations, and best practices.

Quick reference

FeatureSupportedNotes
SELECT, WHERE, GROUP BY, HAVING, ORDER BY, LIMITYes
Column aliases (AS)Yes
Expressions (CASE, CAST, LIKE, BETWEEN, IN, arithmetic)YesFull expression support
EXPLAINYesReturns execution plan as text or JSON
Scalar functionsYesMath, string, datetime, regex, crypto, array, map, struct, JSON
Aggregate functionsYesBasic, approximate, statistical, bitwise, boolean, positional
Approximate aggregatesYesapprox_distinct, approx_median, approx_percentile_cont, approx_top_k
Struct / Array / Map column typesYesBracket notation, get_field(), array functions, map functions
CTEs (WITH ... AS)YesCan reference different tables and include JOINs
JOINs (INNER, LEFT, RIGHT, FULL OUTER, CROSS)YesAll standard join types
Implicit joins (comma FROM)Yes
Subqueries (IN, NOT IN)YesNOT IN not supported on nullable columns — use NOT EXISTS instead
Subqueries (EXISTS, NOT EXISTS)Yessemi-join and anti-join patterns
Scalar subqueriesYesIn SELECT, WHERE, HAVING
Derived tables (FROM subqueries)YesCan be nested and joined. LATERAL derived tables not supported.
Self-joinsYesSame table with different aliases
Window functions (OVER)YesInline OVER (...) only — named WINDOW clause not supported
QUALIFYYesFilter on a window function result
SELECT DISTINCT / DISTINCT ONYes
func(DISTINCT ...)YesCOUNT, SUM, AVG, and other aggregates
Set operations (UNION, UNION ALL, INTERSECT, EXCEPT)Yes
GROUPING SETS / ROLLUP / CUBEYes
OFFSETNo
INSERT / UPDATE / DELETENoRead-only
CREATE / DROP / ALTERNoRead-only

For the full SQL syntax, refer to the SQL reference.


Unsupported SQL features

FeatureError
OFFSETunsupported feature: OFFSET clause is not supported
Named WINDOW clauseunsupported feature: WINDOW clause is not supported
INSERT / UPDATE / DELETEonly read-only queries are allowed
CREATE / DROP / ALTERonly read-only queries are allowed
UNNEST / PIVOT / UNPIVOTNot supported
Wildcard modifiers (ILIKE, EXCLUDE, EXCEPT, REPLACE, RENAME on *)Not supported
Nested (parenthesized) joinsNot supported
LATERAL derived tables / LATERAL VIEWNot supported
PERCENTILE_DISCNot supported — use PERCENTILE_CONT

Unsupported expression patterns

PatternAlternative
NOT IN subquery on nullable columnsUse NOT EXISTS with a correlated subquery instead

Exact aggregates such as COUNT(DISTINCT ...), MEDIAN, PERCENTILE_CONT, ARRAY_AGG, and STRING_AGG are supported. On large datasets, prefer the approximate alternatives (approx_distinct, approx_median, approx_percentile_cont) for lower memory and compute. Refer to Aggregate functions.


Runtime constraints

ConstraintDetails
Resource-intensive queriesDuring open beta, queries that require high memory or compute may time out. This includes multi-way joins (three or more large tables), COUNT(DISTINCT) and other func(DISTINCT ...) across joins or high-cardinality columns, ARRAY_AGG / STRING_AGG, set operations that deduplicate large inputs, window functions over large partitions, and large sorts or high-cardinality GROUP BY. Add WHERE filters and LIMIT, and prefer approx_* aggregates to reduce the chance of a timeout.
Budget-gated functionsMEDIAN, PERCENTILE_CONT, ARRAY_AGG, STRING_AGG, NTH_VALUE used as an aggregate, any aggregate with DISTINCT, and window functions (including those used through QUALIFY) are budget-gated up front. R2 SQL estimates the memory required before running the query and rejects it with a 400 error if too much data would be scanned. Add a GROUP BY or WHERE filters to reduce the rows processed.
Multi-table queriesJOINs, subqueries (IN, EXISTS, scalar, derived tables), and multi-table CTEs are supported. Performance depends on intermediate result size; use WHERE filters to manage join selectivity.
Partitioned and unpartitioned tablesBoth partitioned and unpartitioned Iceberg tables are supported.
Parquet format onlyNo CSV, JSON, or other formats.
Read-onlyR2 SQL is a query engine, not a database. No writes.
now() / current_time() precisionQuantized to 10ms boundaries and forced to UTC.

Common error codes

CodeMeaning
40003Invalid SQL syntax
40004Invalid query (unsupported feature, unknown column, type mismatch)
80001Edge service connection failure (retry)

Best practices

  1. Include time-range filters in WHERE to limit data scanned.
  2. Use specific column names instead of SELECT * for better performance.
  3. Use LIMIT to control result set size.
  4. Use approximate aggregation functions (approx_distinct, approx_median, approx_percentile_cont) instead of exact alternatives on large datasets.
  5. Enable compaction in R2 Data Catalog to reduce the number of files scanned per query.
  6. Use EXPLAIN to inspect the execution plan and verify predicate pushdown.
  7. Use WHERE filters with multi-way joins to reduce intermediate result sizes. Joining three or more large tables without filters can exceed resource limits.
  8. Join large fact tables through dimension tables rather than directly joining two large fact tables. For example, join http_requests to firewall_events through a shared zones dimension rather than cross-joining both fact tables.
  9. Be cautious with COUNT(DISTINCT) across multi-way joins. This combination can produce very large intermediate results. Consider using approx_distinct() or breaking the query into smaller steps.
  10. Use explicit JOIN syntax instead of implicit joins (comma-separated FROM) for readability and to ensure the optimizer can choose optimal join ordering.