SQL reference
R2 SQL is Cloudflare's serverless, distributed, analytics query engine for querying Apache Iceberg ↗ tables stored in R2 Data Catalog. This page documents the supported SQL syntax.
SELECT column_list | expression | aggregation_functionFROM namespace_name.table_name[WHERE conditions][GROUP BY column_list][HAVING conditions][ORDER BY expression [ASC | DESC]][LIMIT number]Lists all available namespaces.
SHOW DATABASES;Alias for SHOW DATABASES. Lists all available namespaces.
SHOW NAMESPACES;Lists all tables within a specific namespace.
SHOW TABLES IN namespace_name;Describes the structure of a table, showing column names and data types.
DESCRIBE namespace_name.table_name;SELECT column_specification [, column_specification, ...]- Column name:
column_name - All columns:
* - Qualified wildcard:
table_name.* - Column alias:
column_name AS alias - Expressions: arithmetic, function calls, CASE expressions, and casts
SELECT * FROM my_namespace.sales_data LIMIT 10SELECT customer_id, region, total_amount FROM my_namespace.sales_data LIMIT 10SELECT region, total_amount * 1.1 AS total_with_tax FROM my_namespace.sales_data LIMIT 10CTEs let you define named temporary result sets using WITH that you can reference in the main query. All CTEs must reference the same single table.
WITH cte_name AS ( SELECT ... FROM namespace_name.table_name [WHERE ...])SELECT ... FROM cte_nameA CTE can reference a previously defined CTE. All CTEs in the chain must derive from the same underlying table.
WITH filtered AS ( SELECT customer_id, department, total_amount FROM my_namespace.sales_data WHERE total_amount > 0),summary AS ( SELECT department, COUNT(*) AS order_count, round(AVG(total_amount), 2) AS avg_amount FROM filtered GROUP BY department)SELECT *FROM summaryWHERE order_count > 100ORDER BY avg_amount DESCSELECT * FROM namespace_name.table_nameR2 SQL queries reference exactly one table, specified as namespace_name.table_name.
SELECT * FROM namespace_name.table_name WHERE condition [AND | OR condition ...]=, !=, <>, <, >, <=, >=
column_name IS NULLcolumn_name IS NOT NULL
IS TRUE,IS FALSE,IS NOT TRUE,IS NOT FALSEIS UNKNOWN,IS NOT UNKNOWN
column_name BETWEEN value1 AND value2column_name NOT BETWEEN value1 AND value2
column_name IN ('value1', 'value2')column_name NOT IN ('value1', 'value2')
column_name LIKE 'pattern'column_name NOT LIKE 'pattern'column_name ILIKE 'pattern'(case-insensitive)column_name NOT ILIKE 'pattern'column_name SIMILAR TO 'regex_pattern'
ANDORNOT
SELECT * FROM my_namespace.sales_dataWHERE timestamp BETWEEN '2025-09-24T01:00:00Z' AND '2025-09-25T01:00:00Z'
SELECT * FROM my_namespace.sales_dataWHERE status = 200 AND response_time > 1000
SELECT * FROM my_namespace.sales_dataWHERE (region = 'North' OR region = 'South') AND total_amount IS NOT NULL
SELECT * FROM my_namespace.sales_dataWHERE department ILIKE '%eng%'SELECT column_list, aggregation_function(column)FROM namespace_name.table_name[WHERE conditions]GROUP BY column_listSELECT department, COUNT(*) AS dept_countFROM my_namespace.sales_dataGROUP BY department
SELECT department, category, SUM(total_amount) AS totalFROM my_namespace.sales_dataGROUP BY department, categorySELECT column_list, aggregation_function(column) AS aliasFROM namespace_name.table_nameGROUP BY column_listHAVING aggregation_function(column) comparison_operator valueSELECT department, COUNT(*) AS dept_countFROM my_namespace.sales_dataGROUP BY departmentHAVING COUNT(*) > 1000
SELECT region, SUM(total_amount) AS totalFROM my_namespace.sales_dataGROUP BY regionHAVING SUM(total_amount) > 1000000ORDER BY expression [ASC | DESC] [, expression [ASC | DESC], ...]- ASC: Ascending order (default)
- DESC: Descending order
- Multi-column ordering is supported
SELECT customer_id, total_amountFROM my_namespace.sales_dataWHERE total_amount IS NOT NULLORDER BY