R2 SQL 是 Cloudflare 的无服务器、分布式分析查询引擎,用于查询存储在 R2 Data Catalog 中的 Apache Iceberg ↗ 表。本页记录了支持的 SQL 语法。
SELECT [DISTINCT] column_list | expression | aggregate_function | window_function
FROM namespace_name.table_name
[JOIN namespace_name.table_name ON condition]
[WHERE conditions]
[GROUP BY column_list]
[HAVING conditions]
[QUALIFY window_condition]
[ORDER BY expression [ASC | DESC]]
[LIMIT number]可以使用集合操作(UNION、UNION ALL、INTERSECT、EXCEPT)组合两个或多个查询。
列出所有可用的命名空间。
SHOW DATABASES;SHOW DATABASES 的别名。列出所有可用的命名空间。
SHOW NAMESPACES;列出特定命名空间内的所有表。
SHOW TABLES IN namespace_name;描述表的结构,显示列名和数据类型。
DESCRIBE namespace_name.table_name;SELECT [DISTINCT] column_specification [, column_specification, ...]- 列名:
column_name - 所有列:
* - 限定通配符:
table_name.* - 列别名:
column_name AS alias - 表达式:算术、函数调用、CASE 表达式和类型转换
SELECT * FROM my_namespace.sales_data LIMIT 10
SELECT customer_id, region, total_amount FROM my_namespace.sales_data LIMIT 10
SELECT region, total_amount * 1.1 AS total_with_tax FROM my_namespace.sales_data LIMIT 10SELECT DISTINCT 返回唯一的行。DISTINCT ON (...) 根据 ORDER BY 子句确定保留哪一行,返回列出的表达式的每个组合的第一行。
-- 唯一的组合
SELECT DISTINCT region, department FROM my_namespace.sales_data
-- 按金额保留每个区域的第一行
SELECT DISTINCT ON (region) region, customer_id, total_amount
FROM my_namespace.sales_data
ORDER BY region, total_amount DESC对于大型数据集上唯一值的计数,approx_distinct() 是更快的选择。
CTE 允许您使用 WITH 定义可在主查询中引用的命名临时结果集。CTE 可以引用不同的表,并且可以包含 JOIN。CTE 也可以在主查询中与其他 CTE 或常规表连接。
WITH cte_name AS (
SELECT ...
FROM namespace_name.table_name
[WHERE ...]
)
SELECT ... FROM cte_nameCTE 可以引用先前定义的 CTE。
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 summary
WHERE order_count > 100
ORDER BY avg_amount DESCWITH enterprise_zones AS (
SELECT zone_id, domain, plan
FROM my_namespace.zones
WHERE plan = 'enterprise'
)
SELECT ez.domain, f.action, COUNT(*) AS cnt
FROM enterprise_zones ez
INNER JOIN my_namespace.firewall_events f ON ez.zone_id = f.zone_id
GROUP BY ez.domain, f.action
ORDER BY cnt DESC
LIMIT 20WITH top_zones AS (
SELECT zone_id, COUNT(*) AS req_count
FROM my_namespace.http_requests
GROUP BY zone_id
ORDER BY req_count DESC
LIMIT 50
),
zone_threats AS (
SELECT zone_id, COUNT(*) AS threat_count
FROM my_namespace.firewall_events
WHERE risk_score > 0.5
GROUP BY zone_id
)
SELECT tz.zone_id, tz.req_count, COALESCE(zt.threat_count, 0) AS threat_count
FROM top_zones tz
LEFT JOIN zone_threats zt ON tz.zone_id = zt.zone_id
ORDER BY tz.req_count DESC
LIMIT 20SELECT * FROM namespace_name.table_nameR2 SQL 查询可以引用一个或多个表。表被指定为 namespace_name.table_name。可以使用 JOIN 或逗号分隔语法组合多个表。有关详细信息,请参阅 JOIN 子句 部分。
R2 SQL 支持在单个查询中连接多个 Iceberg 表。所有连接类型均使用标准 SQL 语法。
| 连接类型 | 语法 | 描述 |
|---|---|---|
| Inner join | INNER JOIN ... ON |
返回在两个表中都匹配的行 |
| Left outer join | LEFT JOIN ... ON |
返回左表的所有行,不匹配的右表行返回 NULL |
| Right outer join | RIGHT JOIN ... ON |
返回右表的所有行,不匹配的左表行返回 NULL |
| Full outer join | FULL OUTER JOIN ... ON |
返回两个表的所有行,没有匹配项的地方返回 NULL |
| Cross join | CROSS JOIN |
两个表的笛卡尔积 |
| Implicit join | FROM t1, t2 WHERE t1.id = t2.id |
在 WHERE 中具有连接条件的逗号分隔表 |
-- 显式 JOIN
SELECT columns
FROM namespace.table1 alias1
[INNER | LEFT | RIGHT | FULL OUTER | CROSS] JOIN namespace.table2 alias2
ON alias1.column = alias2.column
[WHERE conditions]
-- 隐式 join
SELECT columns
FROM namespace.table1 alias1, namespace.table2 alias2
WHERE alias1.column = alias2.column您可以在单个查询中连接三个或更多表:
SELECT z.domain, h.method, f.action, COUNT(*) AS cnt
FROM my_namespace.zones z
INNER JOIN my_namespace.http_requests h ON z.zone_id = h.zone_id
INNER JOIN my_namespace.firewall_events f ON z.zone_id = f.zone_id
WHERE h.status_code >= 400
GROUP BY z.domain, h.method, f.action
ORDER BY cnt DESC
LIMIT 20一个表可以使用不同的别名与自身连接:
SELECT f1.source_ip, f1.zone_id AS zone1, f2.zone_id AS zone2
FROM my_namespace.firewall_events f1
INNER JOIN my_namespace.firewall_events f2
ON f1.source_ip = f2.source_ip
AND f1.zone_id < f2.zone_id
WHERE f1.action = 'block'
LIMIT 20- 连接条件使用带有等号 (
=) 或基于表达式的谓词的ON子句。 - 连接谓词中支持函数(例如,
ON LOWER(a.col) = LOWER(b.col))。 - 可以使用
AND组合多个条件。
- 包含
WHERE过滤器以减少中间结果的大小,特别是对于多向连接。 - 通过共享维度表连接大型事实表,而不是直接交叉连接两个大型表。
- 使用
LIMIT来限制结果大小。
R2 SQL 支持在查询的多个位置使用子查询。
FROM 子句中的子查询会创建一个派生表,可以在外部查询中引用它:
SELECT sub.domain, sub.total_requests
FROM (
SELECT z.domain, COUNT(*) AS total_requests
FROM my_namespace.zones z
INNER JOIN my_namespace.http_requests h ON z.zone_id = h.zone_id
GROUP BY z.domain
) sub
WHERE sub.total_requests > 1000
ORDER BY sub.total_requests DESC
LIMIT 20派生表可以与其他派生表或常规表连接:
SELECT req.domain, req.total_reqs, fw.total_events
FROM (
SELECT zone_id, domain, COUNT(*) AS total_reqs
FROM my_namespace.zones z
INNER JOIN my_namespace.http_requests h ON z.zone_id = h.zone_id
GROUP BY zone_id, domain
) req
INNER JOIN (
SELECT zone_id, COUNT(*) AS total_events
FROM my_namespace.firewall_events
GROUP BY zone_id
) fw ON req.zone_id = fw.zone_id
ORDER BY fw.total_events DESC
LIMIT 20根据值是否存在于子查询的结果中来过滤行:
-- 查找来自企业区域的请求
SELECT method, status_code, COUNT(*) AS cnt
FROM my_namespace.http_requests
WHERE zone_id IN (
SELECT zone_id FROM my_namespace.zones WHERE plan = 'enterprise'
)
GROUP BY method, status_code
ORDER BY cnt DESC
LIMIT 20-- NOT IN 示例
SELECT zone_id, COUNT(*) AS cnt
FROM my_namespace.http_requests
WHERE zone_id NOT IN (
SELECT zone_id FROM my_namespace.firewall_events WHERE action = 'block'
)
GROUP BY zone_id
LIMIT 10测试匹配相关条件的行是否存在:
-- 查找具有被阻止防火墙事件的区域 (半连接)
SELECT z.domain, z.plan
FROM my_namespace.zones z
WHERE EXISTS (
SELECT 1 FROM my_namespace.firewall_events f
WHERE f.zone_id = z.zone_id AND f.action = 'block'
)
ORDER BY z.domain
LIMIT 20-- 查找没有防火墙事件的区域 (反连接)
SELECT z.domain, z.plan
FROM my_namespace.zones z
WHERE NOT EXISTS (
SELECT 1 FROM my_namespace.firewall_events f
WHERE f.zone_id = z.zone_id
)
ORDER BY z.domain
LIMIT 20返回单个值的子查询可以用于 SELECT、WHERE 或 HAVING:
-- 在 SELECT 中(每行的常量值)
SELECT z.domain, z.plan,
(SELECT COUNT(*) FROM my_namespace.zones) AS total_zones
FROM my_namespace.zones z
WHERE z.plan = 'enterprise'
LIMIT 10-- 在 WHERE 中(比较)
SELECT z.domain, z.plan, z.requests_30d
FROM my_namespace.zones z
WHERE z.requests_30d > (
SELECT AVG(requests_30d) FROM my_namespace.zones
)
ORDER BY z.requests_30d DESC
LIMIT 20SELECT * 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'(不区分大小写)column_name NOT ILIKE 'pattern'column_name SIMILAR TO 'regex_pattern'
ANDORNOT
SELECT * FROM my_namespace.sales_data
WHERE timestamp BETWEEN '2025-09-24T01:00:00Z' AND '2025-09-25T01:00:00Z'
SELECT * FROM my_namespace.sales_data
WHERE status = 200 AND response_time > 1000
SELECT * FROM my_namespace.sales_data
WHERE (region = 'North' OR region = 'South')
AND total_amount IS NOT NULL
SELECT * FROM my_namespace.sales_data
WHERE department ILIKE '%eng%'SELECT column_list, aggregation_function(column)
FROM namespace_name.table_name
[WHERE conditions]
GROUP BY column_listSELECT department, COUNT(*) AS dept_count
FROM my_namespace.sales_data
GROUP BY department
SELECT department, category, SUM(total_amount) AS total
FROM my_namespace.sales_data
GROUP BY department, category这些扩展在单个查询中计算多个分组,包括小计和总计。
GROUPING SETS:精确计算您列出的分组。()生成总计。ROLLUP:从左到右计算层次化的小计。ROLLUP(a, b)按(a, b)、(a)和()进行分组。CUBE:计算列出列的每种组合。CUBE(a, b)按(a, b)、(a)、(b)和()进行分组。
-- 每个部门的小计加上一个总计
SELECT department, SUM(total_amount) AS total
FROM my_namespace.sales_data
GROUP BY ROLLUP(department)
-- 部门和类别的每种组合
SELECT department, category, SUM(total_amount) AS total
FROM my_namespace.sales_data
GROUP BY CUBE(department, category)
-- 显式分组
SELECT department, category, SUM(total_amount) AS total
FROM my_namespace.sales_data
GROUP BY GROUPING SETS ((department, category), (department), ())SELECT column_list, aggregation_function(column) AS alias
FROM namespace_name.table_name
GROUP BY column_list
HAVING aggregation_function(column) comparison_operator valueSELECT department, COUNT(*) AS dept_count
FROM my_namespace.sales_data
GROUP BY department
HAVING COUNT(*) > 1000
SELECT region, SUM(total_amount) AS total
FROM my_namespace.sales_data
GROUP BY region
HAVING SUM(total_amount) > 1000000ORDER BY expression [ASC | DESC] [, expression [ASC | DESC], ...]- ASC:升序(默认)
- DESC:降序
- 支持多列排序
SELECT customer_id, total_amount
FROM my_namespace.sales_data
WHERE total_amount IS NOT NULL
ORDER BY total_amount DESC
LIMIT 50
SELECT department, COUNT(*) AS dept_count
FROM my_namespace.sales_data
GROUP BY department
ORDER BY dept_count DESC, department ASCLIMIT number- 类型:仅限整数
- 默认值:500
SELECT * FROM my_namespace.sales_data LIMIT 100窗口函数计算与当前行相关的一组行的值,而不会将它们折叠为单个输出行。窗口使用包含可选的 PARTITION BY、ORDER BY 和帧规范的 OVER (...) 子句内联定义。
function(args) OVER (
[PARTITION BY expression [, ...]]
[ORDER BY expression [ASC | DESC] [, ...]]
[frame_specification]
)| 类别 | 函数 |
|---|---|
| 排名 | ROW_NUMBER, RANK, DENSE_RANK, PERCENT_RANK, CUME_DIST, NTILE |
| 偏移 | LAG, LEAD, FIRST_VALUE, LAST_VALUE, NTH_VALUE |
| 聚合 | SUM, AVG, COUNT, MIN, MAX, 以及其他与 OVER 一起使用的聚合函数 |
-- 在每个分区内对行进行排名
SELECT customer_id, region,
ROW_NUMBER() OVER (PARTITION BY region ORDER BY total_amount DESC) AS rank_in_region,
LAG(total_amount) OVER (PARTITION BY region ORDER BY total_amount DESC) AS prev_amount
FROM my_namespace.sales_data
-- 带有显式帧的运行总计
SELECT customer_id, total_amount,
SUM(total_amount) OVER (ORDER BY total_amount ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS running_total
FROM my_namespace.sales_dataQUALIFY 根据窗口函数的结果过滤行,类似于 HAVING 如何过滤分组行。
-- 仅保留每个区域中金额排名前 3 的客户
SELECT customer_id, region, total_amount
FROM my_namespace.sales_data
QUALIFY ROW_NUMBER() OVER (PARTITION BY region ORDER BY total_amount DESC) <= 3集合操作组合两个或多个 SELECT 语句的结果。
SELECT ... FROM table1
UNION | UNION ALL | INTERSECT | EXCEPT
SELECT ... FROM table2| 操作 | 描述 |
|---|---|
UNION |
返回两个查询的所有行,并删除重复项 |
UNION ALL |
返回两个查询的所有行,包括重复项 |
INTERSECT |
仅返回同时出现在两个查询结果中的行 |
EXCEPT |
返回出现在第一个查询中但未出现在第二个查询中的行 |
-- 查找具有防火墙阻止或高风险请求的区域
SELECT zone_id FROM my_namespace.firewall_events WHERE action = 'block'
UNION
SELECT zone_id FROM my_namespace.http_requests WHERE risk_score > 0.8-- 查找既有防火墙阻止又有区域表条目的区域
SELECT zone_id FROM my_namespace.firewall_events WHERE action = 'block'
INTERSECT
SELECT zone_id FROM my_namespace.zones WHERE plan = 'enterprise'-- 查找没有防火墙事件的企业区域
SELECT zone_id FROM my_namespace.zones WHERE plan = 'enterprise'
EXCEPT
SELECT zone_id FROM my_namespace.firewall_events- 集合操作中的所有查询必须返回相同数量的列。
- 相应的列必须具有兼容的数据类型。
- 结果中的列名取自第一个查询。
返回查询的执行计划而不运行它。
EXPLAIN SELECT department, COUNT(*) AS dept_count
FROM my_namespace.sales_data
WHERE total_amount IS NOT NULL
GROUP BY department;以结构化 JSON 格式返回执行计划以进行编程式分析。
EXPLAIN FORMAT JSON SELECT * FROM my_namespace.sales_data LIMIT 10;表达式可用于 SELECT、WHERE、GROUP BY、HAVING 和 ORDER BY 子句。
SELECT 42 AS int_val, 3.14 AS float_val, 'hello' AS str_val, TRUE AS bool_val, NULL AS null_val
FROM my_namespace.sales_data LIMIT 1+, -, *, /, %
SELECT customer_id, total_amount * 1.1 AS total_with_tax, total_amount % 10 AS remainder
FROM my_namespace.sales_data
WHERE total_amount IS NOT NULL
LIMIT 5SELECT customer_id || ' - ' || region AS label
FROM my_namespace.sales_data
LIMIT 5搜索形式:
SELECT customer_id,
CASE
WHEN total_amount > 1000 THEN 'high'
WHEN total_amount > 100 THEN 'medium'
ELSE 'low'
END AS tier
FROM my_namespace.sales_data
LIMIT 10简单形式:
SELECT customer_id,
CASE region
WHEN 'North' THEN 'N'
WHEN 'South' THEN 'S'
ELSE 'Other'
END AS region_code
FROM my_namespace.sales_data
LIMIT 10-- CAST
SELECT CAST(total_amount AS INT) AS amount_int FROM my_namespace.sales_data LIMIT 5
-- TRY_CAST (在失败时返回 NULL 而不是错误)
SELECT TRY_CAST(customer_id AS INT) AS id_int FROM my_namespace.sales_data LIMIT 5
-- 简写 (::)
SELECT total_amount::INT AS amount_int FROM my_namespace.sales_data LIMIT 5SELECT EXTRACT(YEAR FROM timestamp) AS yr,
EXTRACT(MONTH FROM timestamp) AS mo,
EXTRACT(DAY FROM timestamp) AS dy
FROM my_namespace.sales_data
LIMIT 1| 类型 | 描述 | 示例值 |
|---|---|---|
integer |
整数 | 1, 42, -10, 0 |
float |
小数 | 1.5, 3.14, -2.7, 0.0 |
string |
文本值 | 'hello', 'GET', '2024-01-01' |
boolean |
布尔值 | true, false |
timestamp |
RFC3339 | '2025-09-24T01:00:00Z' |
date |
日期值 | '2025-09-24' |
struct |
命名字段 | struct_col['field_name'] |
array |
有序列表 | array_col[1] (从 1 开始) |
map |
键值对 | map_keys(map_col) |
- 比较运算符:
=,!=,<,<=,>,>=,LIKE,BETWEEN,IS NULL,IS NOT NULL - AND (较高优先级)
- OR (较低优先级)
使用括号来覆盖默认优先级:
SELECT * FROM my_namespace.sales_data WHERE (status = 404 OR status = 500) AND region = 'North'SELECT *
FROM my_namespace.sales_data
WHERE timestamp BETWEEN '2025-09-24T01:00:00Z' AND '2025-09-25T01:00:00Z'
LIMIT 100SELECT customer_id, timestamp, status, total_amount
FROM my_namespace.sales_data
WHERE status >= 400 AND total_amount > 5000
ORDER BY total_amount DESC
LIMIT 50SELECT region, COUNT(*) AS region_count, AVG(total_amount) AS avg_amount
FROM my_namespace.sales_data
WHERE status = 'completed'
GROUP BY region
HAVING COUNT(*) > 1000
ORDER BY avg_amount DESC
LIMIT 20SELECT customer_id,
CASE
WHEN total_amount >= 1000 THEN 'Premium'
WHEN total_amount >= 100 THEN 'Standard'
ELSE 'Basic'
END AS tier,
total_amount
FROM my_namespace.sales_data
WHERE total_amount IS NOT NULL
ORDER BY total_amount DESC
LIMIT 20