INTEGRITY Документация

справочник по SQL

R2 SQL представляет собой бессерверный распределённый аналитический движок запросов Cloudflare, предназначенный для запросов к Apache Iceberg ↗ таблиц, хранящихся в R2 Data Catalog. На этой странице описан поддерживаемый синтаксис 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 DATABASES. Выводит список всех доступных пространств имён.

SHOW NAMESPACES;

SHOW TABLES

Выводит список всех таблиц в указанном пространстве имён.

SHOW TABLES IN namespace_name;

DESCRIBE

Описывает структуру таблицы: имена столбцов и типы данных.

DESCRIBE namespace_name.table_name;

Секция SELECT

Синтаксис

SELECT [DISTINCT] column_specification [, column_specification, ...]

Спецификация столбца

Примеры

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 10

DISTINCT

SELECT DISTINCT возвращает уникальные строки. DISTINCT ON (...) возвращает первую строку для каждой комбинации перечисленных выражений, используя ORDER BY предложение, чтобы определить, какая строка сохраняется.

-- Unique combinations
SELECT DISTINCT region, department FROM my_namespace.sales_data

-- First row per region by amount
SELECT DISTINCT ON (region) region, customer_id, total_amount
FROM my_namespace.sales_data
ORDER BY region, total_amount DESC

Для подсчёта уникальных значений в больших наборах данных, approx_distinct() является более быстрой альтернативой.


Обобщённые табличные выражения (CTE)

CTE позволяют определять именованные временные наборы результатов с помощью WITH на которое можно ссылаться в основном запросе. CTE могут ссылаться на разные таблицы и включать JOIN. CTE также можно объединить с другими CTE или обычными таблицами в основном запросе.

Синтаксис

WITH cte_name AS (
    SELECT ...
    FROM namespace_name.table_name
    [WHERE ...]
)
SELECT ... FROM cte_name

Цепочки CTE

CTE может ссылаться на ранее определённый 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 DESC

CTE, соединённый с другой таблицей

WITH 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 20

Два соединённых CTE

WITH 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 20

Секция FROM

Синтаксис

SELECT * FROM namespace_name.table_name

Запросы R2 SQL могут ссылаться на одну или несколько таблиц. Таблицы указываются в виде namespace_name.table_name. Несколько таблиц можно объединить с помощью JOIN или синтаксиса через запятую. См. Предложение JOIN раздел для получения подробной информации.


Предложение JOIN

R2 SQL поддерживает объединение нескольких таблиц Iceberg в одном запросе. Все типы соединений используют стандартный синтаксис SQL.

Поддерживаемые типы соединений

Тип соединения Синтаксис Описание
Внутреннее соединение INNER JOIN ... ON Возвращает строки, совпадающие в обеих таблицах
Левое внешнее соединение LEFT JOIN ... ON Возвращает все строки из левой таблицы, подставляя NULL для несовпадающих строк правой
Правое внешнее соединение RIGHT JOIN ... ON Возвращает все строки из правой таблицы, подставляя NULL для несовпадающих строк левой
Полное внешнее соединение FULL OUTER JOIN ... ON Возвращает все строки из обеих таблиц, подставляя NULL при отсутствии совпадений
Перекрёстное соединение CROSS JOIN Декартово произведение двух таблиц
Неявное соединение FROM t1, t2 WHERE t1.id = t2.id Таблицы, перечисленные через запятую, с условием соединения в WHERE

Синтаксис

-- Explicit JOIN
SELECT columns
FROM namespace.table1 alias1
[INNER | LEFT | RIGHT | FULL OUTER | CROSS] JOIN namespace.table2 alias2
  ON alias1.column = alias2.column
[WHERE conditions]

-- Implicit 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

Условия соединения

Рекомендации по использованию соединений


Подзапросы

R2 SQL поддерживает подзапросы в нескольких позициях внутри запроса.

Подзапросы в FROM (производные таблицы)

Подзапрос в 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

IN / NOT IN подзапросы

Фильтрация строк по наличию значения в результате подзапроса:

-- Find requests from enterprise zones
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 example
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

EXISTS / NOT EXISTS подзапросы

Проверка наличия строк, соответствующих коррелированному условию:

-- Find zones with blocked firewall events (semi-join)
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
-- Find zones with NO firewall events (anti-join)
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:

-- In SELECT (constant value per row)
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
-- In WHERE (comparison)
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 20

Секция WHERE

Синтаксис

SELECT * FROM namespace_name.table_name WHERE condition [AND | OR condition ...]

Условия

Операторы сравнения

=, !=, <>, <, >, <=, >=

Проверки на NULL

Логические проверки

Диапазон

Проверка вхождения в список

Сопоставление с шаблоном

Логические операторы

Примеры

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%'

Секция GROUP BY

Синтаксис

SELECT column_list, aggregation_function(column)
FROM namespace_name.table_name
[WHERE conditions]
GROUP BY column_list

Примеры

SELECT 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 и CUBE

Эти расширения вычисляют несколько группировок, включая промежуточные и общие итоги, в рамках одного запроса.

-- Subtotals per department plus a grand total
SELECT department, SUM(total_amount) AS total
FROM my_namespace.sales_data
GROUP BY ROLLUP(department)

-- Every combination of department and category
SELECT department, category, SUM(total_amount) AS total
FROM my_namespace.sales_data
GROUP BY CUBE(department, category)

-- Explicit groupings
SELECT department, category, SUM(total_amount) AS total
FROM my_namespace.sales_data
GROUP BY GROUPING SETS ((department, category), (department), ())

Предложение HAVING

Синтаксис

SELECT column_list, aggregation_function(column) AS alias
FROM namespace_name.table_name
GROUP BY column_list
HAVING aggregation_function(column) comparison_operator value

Примеры

SELECT 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) > 1000000

Секция ORDER BY

Синтаксис

ORDER BY expression [ASC | DESC] [, expression [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 ASC

Условие LIMIT

Синтаксис

LIMIT number

Примеры

SELECT * FROM my_namespace.sales_data LIMIT 100

Оконные функции

Оконные функции вычисляют значение по набору строк, связанных с текущей строкой, не сворачивая их в одну итоговую строку. Окно задаётся непосредственно с помощью OVER (...) предложение, содержащее необязательный PARTITION BY, ORDER BY, и спецификации фрейма.

Синтаксис

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

Примеры

-- Rank rows within each partition
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

-- Running total with an explicit frame
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_data

QUALIFY

QUALIFY фильтрует строки на основе результата оконной функции, аналогично тому, как HAVING фильтрует сгруппированные строки.

-- Keep only the top 3 customers by amount in each region
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 Возвращает строки из первого запроса, отсутствующие во втором

Примеры

Объединение

-- Find zones that had either firewall blocks OR high-risk requests
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

Intersect

-- Find zones with both firewall blocks AND entries in the zones table
SELECT zone_id FROM my_namespace.firewall_events WHERE action = 'block'
INTERSECT
SELECT zone_id FROM my_namespace.zones WHERE plan = 'enterprise'

Except

-- Find enterprise zones that have no firewall events
SELECT zone_id FROM my_namespace.zones WHERE plan = 'enterprise'
EXCEPT
SELECT zone_id FROM my_namespace.firewall_events

Требования


EXPLAIN

Возвращает план выполнения запроса без его фактического выполнения.

EXPLAIN SELECT department, COUNT(*) AS dept_count
FROM my_namespace.sales_data
WHERE total_amount IS NOT NULL
GROUP BY department;

EXPLAIN FORMAT JSON

Возвращает план выполнения в виде структурированного 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 5

Конкатенация строк

SELECT customer_id || ' - ' || region AS label
FROM my_namespace.sales_data
LIMIT 5

Выражения CASE

Форма с условием:

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 (returns NULL on failure instead of error)
SELECT TRY_CAST(customer_id AS INT) AS id_int FROM my_namespace.sales_data LIMIT 5

-- Shorthand (::)
SELECT total_amount::INT AS amount_int FROM my_namespace.sales_data LIMIT 5

EXTRACT

SELECT 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)

Приоритет операторов

  1. Операторы сравнения: =, !=, <, <=, >, >=, LIKE, BETWEEN, IS NULL, IS NOT NULL
  2. AND (более высокий приоритет)
  3. 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 100

Отфильтрованный запрос с сортировкой

SELECT customer_id, timestamp, status, total_amount
FROM my_namespace.sales_data
WHERE status >= 400 AND total_amount > 5000
ORDER BY total_amount DESC
LIMIT 50

Агрегация с HAVING

SELECT 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 20

Условная категоризация

SELECT 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