SQL-запросы и каталог в BigQuery
Выполняет SQL в Google BigQuery, следит за статусом заданий, листает результаты и показывает наборы данных, таблицы и их схемы.
- Что делает
- Выполняет SQL в Google BigQuery, следит за статусом заданий, листает результаты и показывает наборы данных, таблицы и их схемы.
- Когда брать
- Когда нужно сделать запрос к таблице BigQuery, узнать, что лежит в наборе данных, проверить статус задания или разобраться в схеме таблицы.
- Когда не брать
- Если BigQuery не подключён к среде выполнения или у вас нет прав на проект и набор данных.
- Пример запроса
- Посчитай в BigQuery число заказов по месяцам из таблицы shop.sales.orders за этот год.
- Нужно подключить
- BigQuery, терминал
Входит в плагин bigquery. В Cowork и Claude Code можно поставить плагин целиком.
Как включить
- Скачайте архив и распакуйте его.
- Положите папку
bigquery-apiв~/.claude/skills/. - Откройте Claude Code и опишите задачу своими словами: Claude подхватит скилл по описанию.
Текст
---
name: bigquery-api
description: Выполняет SQL в Google BigQuery и просматривает его каталог — отправляет запросы (синхронно или асинхронно), опрашивает статус заданий, листает результаты, выводит список наборов данных и таблиц и читает схемы таблиц. Используй всякий раз, когда пользователь хочет сделать запрос к таблице BigQuery, спрашивает «что в этом наборе данных», проверяет статус задания BigQuery или упоминает bigquery.googleapis.com либо путь вида `project.dataset.table`. Всегда начинай с этого скилла при работе с этим сервисом — его встроенные скрипты и рецепты — самый быстрый путь.
---
REST API BigQuery (bigquery.googleapis.com/bigquery/v2) позволяет выполнять SQL, смотреть задания и просматривать наборы данных и схемы таблиц обычным curl — SDK не нужен.
Центральное понятие — задание (job). Каждый запрос выполняется как задание в проекте (проект оплачивается и не обязательно тот, где лежат данные). Внутри запрос идёт одним из двух способов — scripts/bq_query.sh (операция 1) ведёт его за тебя:
- Синхронный (
jobs.query) — один POST, который блокируется до таймаута и возвращает строки сразу, если запрос успел завершиться. - Асинхронный (
jobs.insert→jobs.get→jobs.getQueryResults) — отправить, опрашивать, затем листать результаты. Этим путём пользуйся напрямую для таблиц назначения, приоритетаBATCHи заданий загрузки, выгрузки и копирования.
Всё остальное (наборы данных, таблицы, схемы) — простой GET.
Настройка запросов
Аутентификацию обеспечивает среда выполнения — учётные данные подставляются в исходящие запросы к этому API, поэтому настраивать ничего не нужно. Не пытайся создавать, выпускать, обновлять или проверять токены и ключи. Переменные с учётными данными нужны только для того, чтобы запросы были корректно составлены; если какая-то не задана, присвой ей любое значение-заглушку. Устойчивая ошибка 401/403 означает, что учётные данные для этого рабочего пространства не настроены, — сообщи об этом, а не разбирайся с аутентификацией.
Каждый запрос несёт Authorization: Bearer ... и привязан к расчётному проекту:
export GCP_PROJECT="my-project" # the project that pays for queries — must be real
export BQ_TOKEN="placeholder" # injected by the runtime; any value works
Определи вспомогательную функцию, чтобы не повторять базовый URL и заголовок авторизации в каждом вызове:
export BQ="https://bigquery.googleapis.com/bigquery/v2/projects/${GCP_PROJECT}"
bq_curl() { curl -sS "$@" -H "Authorization: Bearer ${BQ_TOKEN}"; }
Проверка на здравый смысл — убедись, что проект правильный и рабочее пространство подключено:
bq_curl "${BQ}/datasets?maxResults=1" | jq .
# 200 with a "datasets" array on success (the key is omitted entirely when the project
# has no datasets — that's still a success); 401/403 otherwise.
Основные операции
1. Выполнить запрос (scripts/bq_query.sh)
Запускай SQL через встроенный скрипт (путь указан относительно каталога этого скилла): он отправляет запрос, опрашивает до завершения с передачей location, проходит по всем страницам результата и декодирует кодировку ячеек f/v для скалярных столбцов (вложенные и повторяющиеся столбцы выдаются как необработанный JSON f/v — при необходимости разверни их с помощью jq).
scripts/bq_query.sh \
'SELECT name, SUM(number) AS total
FROM `bigquery-public-data.usa_names.usa_1910_current`
WHERE state = @state GROUP BY name ORDER BY total DESC LIMIT 10' \
--param state=STRING:CA --max-gb 5
- SQL — один аргумент (бери его в одинарные кавычки, чтобы имена таблиц в обратных апострофах пережили оболочку) или stdin. Параметры экземпляра берутся из
GCP_PROJECT/BQ_TOKENвыше;--projectпереопределяет расчётный проект. --param NAME=TYPE:VALUE(можно повторять) передаёт именованные параметры запроса — предпочитай его подстановке значений в строку SQL.--dry-runпечатает, сколько байт запрос просканировал бы, не выполняя его;--max-gb Nзаставляет запрос завершиться ошибкой, а не сканировать больше N ГиБ.--max-rows Nограничивает число получаемых строк (по умолчанию 10000,0= все);--jsonвыдаёт по одному JSON-объекту на строку вместо TSV с заголовком. Идентификатор задания, число просканированных байт и числа строк идут в stderr.- Коды возврата:
0— успех,1— запрос или его выполнение не удались (сообщение API в stderr),2— ожидание прекращено после--max-waitсекунд (по умолчанию 600) — идентификатор задания в stderr; опрашивай его конечными точками ниже.
Если скрипт выдаёт ошибку, прочитай его — это обычные curl + jq — и отлаживай по references/api.md. Для параметров-массивов и структур, таблиц назначения или режима записи, приоритета BATCH и заданий не-запросов (загрузка, выгрузка, копирование) используй jobs.insert напрямую (следующая операция).
2. Отправить задание напрямую (jobs.insert → jobs.get)
Когда нужны таблица назначения, режим записи, приоритет BATCH или задание не-запрос (загрузка, выгрузка, копирование), отправь задание сам и опрашивай. POST ${BQ}/jobs сразу возвращает jobReference:
JOB=$(bq_curl -X POST "${BQ}/jobs" -H "Content-Type: application/json" \
-d '{"configuration": {"query": {"query": "SELECT ...", "useLegacySql": false}}}')
JOB_ID=$(jq -r '.jobReference.jobId // empty' <<<"$JOB")
LOCATION=$(jq -r '.jobReference.location // empty' <<<"$JOB")
Затем опрашивай GET ${BQ}/jobs/${JOB_ID}?location=${LOCATION}, пока .status.state не станет DONE — делай паузы между вызовами и ограничивай цикл (эталонная реализация — scripts/bq_query.sh). Полное тело configuration.query — таблица назначения, режим записи, maximumBytesBilled, приоритет, параметры — описано в references/api.md, раздел Query job configuration.
- Если
JOB_IDвернулся пустым, не удалась сама вставка; покажи.errorвместо опроса. - Всегда передавай
locationпри опросе — задание привязано к тому расположению, где оно выполнялось, и без него можно получить 404 для наборов данных вне США. - Задание в состоянии
DONEвсё равно могло завершиться ошибкой: проверь.status.errorResult, прежде чем получать результаты. - Строки результата задания-запроса получай через
GET ${BQ}/queries/${JOB_ID}?location=${LOCATION}&maxResults=1000— ячейки приходят как{"f": [{"v": "..."}]}в порядке схемы (имена столбцов в.schema.fields[].name); следующие страницы определяет.pageToken(см. «Постраничность»).
3. Отменить выполняющееся задание
bq_curl -X POST "${BQ}/jobs/${JOB_ID}/cancel?location=${LOCATION}" | jq '.job.status // .error'
Отмена выполняется по мере возможности; чтобы убедиться, опроси jobs.get.
4. Список недавних заданий
bq_curl "${BQ}/jobs?maxResults=20&projection=full&allUsers=false&stateFilter=done" \
| jq '.jobs[]? | {id: .jobReference.jobId, state: .status.state, query: (.configuration.query.query // "" | .[0:80]), bytes: .statistics.query.totalBytesProcessed}'
5. Список наборов данных
bq_curl "${BQ}/datasets?maxResults=100" \
| jq '.datasets[]? | {id: .datasetReference.datasetId, location}'
Передай ?all=true, чтобы включить скрытые наборы данных. Чтобы увидеть наборы другого проекта, подставь проект в URL (там нужно разрешение bigquery.datasets.get).
6. Список таблиц в наборе данных
DATASET="my_dataset"
bq_curl "${BQ}/datasets/${DATASET}/tables?maxResults=100" \
| jq '.tables[]? | {id: .tableReference.tableId, type, creationTime}'
type — это TABLE, VIEW, EXTERNAL, MATERIALIZED_VIEW или SNAPSHOT.
7. Получить схему и размер таблицы
TABLE="events"
bq_curl "${BQ}/datasets/${DATASET}/tables/${TABLE}" \
| jq '{rows: .numRows, bytes: .numBytes, partitioning: .timePartitioning, schema: [.schema.fields[]? | {name, type, mode}]}'
Вложенные столбцы выглядят как type: "RECORD" со своими fields[] — рекурсивно разворачивай, если нужно полное дерево.
8. Просмотр строк таблицы без запроса (tabledata.list)
Читает строки прямо из хранилища — без задания-запроса и без платы за просканированные байты.
bq_curl "${BQ}/datasets/${DATASET}/tables/${TABLE}/data?maxResults=10" \
| jq '.rows[]?.f | map(.v)'
Постраничность
Все конечные точки со списками используют одну схему: если есть продолжение, ответ несёт токен, и ты передаёшь его обратно как ?pageToken= в следующем вызове. Остановись, когда поля нет. maxResults ограничивает одну страницу; реальные пределы API определяются размером (~10 МБ на страницу tabledata.list, ~20 МБ на страницу getQueryResults), а не фиксированным числом строк — встроенный скрипт по умолчанию берёт 1000 строк на страницу.
Название поля в ответе неодинаково — проверь, какое возвращает твоя конечная точка:
pageToken—jobs.query,jobs.getQueryResults,tabledata.list.nextPageToken—datasets.list,tables.list,jobs.list,routines.list,models.list.
Чтение .pageToken в ответе datasets.list молча даёт null, и ты получаешь только одну страницу.
У результатов запросов есть ещё одна тонкость: jobs.query и jobs.getQueryResults также возвращают totalRows — полное число строк, даже если отдельная страница меньше; используй его для индикаторов прогресса, а не как условие остановки.
Лимиты и квоты
BigQuery применяет квоты на проект, а не ограничения частоты по отдельным запросам. Вот те, на которые ты наткнёшься:
- Параллельные запросы: BigQuery сам решает, сколько запросов выполнять одновременно (динамический параллелизм), и ставит остальные в очередь — до 1000 интерактивных запросов в очереди на проект в регионе. Сверх этого отправка завершается ошибкой
quotaExceeded/jobRateLimitExceeded. - Запросы к API: большинство методов ограничено примерно 100 запросами в секунду на пользователя на метод (
jobs.getиtabledata.listдопускают больше) — опрашивай сsleep, а не в плотном цикле. - Квоты на просканированные байты по запросу (on-demand) и слот-время зависят от вашей модели оплаты.
Ответы 429 и 403 rateLimitExceeded / quotaExceeded несут причину, допускающую повтор, в error.errors[].reason. Делай экспоненциальную паузу и повторяй; не сокращай интервал опроса.
Обработка ошибок
Каждый ответ с ошибкой имеет вид {"error": {"code": N, "message": "...", "errors": [{"reason": "..."}]}} — проверяй .error, прежде чем выбирать поля. Строка reason — самое полезное поле.
401— учётные данные отсутствуют или отклонены. Проверь, чтоBQ_TOKENвообще задан (подойдёт любое значение). Если не проходит, учётные данные для этого рабочего пространства не настроены — сообщи об этом.403 accessDenied— у вызывающего нет прав на проект или набор данных. Проверь, какой проект указан в URL (расчётный) и какой владеет данными — они могут различаться. Выдайroles/bigquery.dataViewerна данные иroles/bigquery.jobUserна расчётный проект.404 notFound— задания, набора данных или таблицы не существует, либо при поиске задания ты опустилlocation. Перепроверь полную ссылку (project.dataset.table). Всегда передавай?location=вjobs.get/getQueryResults.400 invalidQuery— ошибка SQL. Вmessageуказаны строка и столбец. Помни проuseLegacySql: false.status.errorResultзадания — запрос выполнился, но завершился ошибкой. Задание может бытьDONEи всё же неудачным — всегда проверяйstatus.errorResult, прежде чем получать результаты.5xx/backendError— временный сбой на стороне Google. Повторяй с увеличивающейся паузой. Для заданий чтения это безопасно; для заданий записи проверь, выполнилась ли первая попытка на самом деле, прежде чем повторять.
Углубиться
В references/api.md есть более полный каталог конечных точек — объект конфигурации задания в деталях (таблицы назначения, режим записи, задания загрузки, выгрузки и копирования), создание и обновление наборов данных и таблиц, процедуры (routines) и политики доступа на уровне строк. Читай его, когда нужна конечная точка, не описанная выше, или точная форма тела для записи.
Перевод: iiuniversitet. Оригинал: https://github.com/anthropics/claude-tag-plugins/tree/main/bigquery/skills/bigquery-api, лицензия Apache-2.0. Изменения: перевод на русский язык.
Оригинал на английском
---
name: bigquery-api
description: Run SQL against Google BigQuery and browse its catalog — submit queries (sync or async), poll job status, page through results, list datasets/tables, and read table schemas. Use this whenever the user wants to query a BigQuery table, ask "what's in this dataset", check a BigQuery job's status, or mentions bigquery.googleapis.com or a `project.dataset.table` path. Always start from this skill when interacting with this service — its bundled scripts and recipes are the fastest path.
---
BigQuery's REST API (`bigquery.googleapis.com/bigquery/v2`) lets you run SQL, inspect jobs, and browse datasets and table schemas with plain `curl` — no SDK required.
The central concept is a **job**. Every query runs as a job in a **project** (the project is billed, and is not necessarily where the data lives). Under the hood a query runs one of two ways — `scripts/bq_query.sh` (operation 1) drives this for you:
- **Synchronous** (`jobs.query`) — one POST that blocks up to a timeout and returns rows inline if the query finishes in time.
- **Asynchronous** (`jobs.insert` → `jobs.get` → `jobs.getQueryResults`) — submit, poll, then page results. The route you use directly for destination tables, `BATCH` priority, or load/extract/copy jobs.
Everything else (datasets, tables, schemas) is a simple GET.
## Request setup
Authentication is handled by the runtime — credentials are injected into outbound requests to this API, so there is nothing to set up. Do not try to create, mint, refresh, or validate tokens or keys. Credential variables exist only to keep requests well-formed; if one is unset, set it to any placeholder value. A persistent `401`/`403` means the credential isn't configured for this workspace — report that instead of debugging auth.
Every request carries `Authorization: Bearer ...` and is rooted at a **billing project**:
```bash
export GCP_PROJECT="my-project" # the project that pays for queries — must be real
export BQ_TOKEN="placeholder" # injected by the runtime; any value works
```
Define a helper to avoid repeating the base URL and auth header on every call:
```bash
export BQ="https://bigquery.googleapis.com/bigquery/v2/projects/${GCP_PROJECT}"
bq_curl() { curl -sS "$@" -H "Authorization: Bearer ${BQ_TOKEN}"; }
```
**Sanity check** — confirm the project is right and the workspace is wired up:
```bash
bq_curl "${BQ}/datasets?maxResults=1" | jq .
# 200 with a "datasets" array on success (the key is omitted entirely when the project
# has no datasets — that's still a success); 401/403 otherwise.
```
## Core operations
### 1. Run a query (`scripts/bq_query.sh`)
Run SQL through the bundled script (path is relative to this skill's directory): it submits the
query, polls until done with `location` threaded through, pages through every result page, and
decodes the `f`/`v` cell encoding for scalar columns (nested/repeated columns are emitted as raw
`f`/`v` JSON — post-process with `jq` if you need them flattened).
```bash
scripts/bq_query.sh \
'SELECT name, SUM(number) AS total
FROM `bigquery-public-data.usa_names.usa_1910_current`
WHERE state = @state GROUP BY name ORDER BY total DESC LIMIT 10' \
--param state=STRING:CA --max-gb 5
```
- SQL is one argument (single-quote it so backticked table names survive the shell) or stdin.
Instance specifics come from `GCP_PROJECT` / `BQ_TOKEN` above; `--project` overrides the billing
project.
- `--param NAME=TYPE:VALUE` (repeatable) sends named query parameters — prefer it over
interpolating values into the SQL string.
- `--dry-run` prints the bytes a query would scan without running it; `--max-gb N` makes the query
fail rather than scan more than N GiB.
- `--max-rows N` caps fetched rows (default 10000, `0` = everything); `--json` emits one JSON
object per row instead of TSV with a header. Job ID, bytes scanned, and row counts go to stderr.
- Exit codes: `0` success, `1` request or query failed (API message on stderr), `2` gave up waiting
after `--max-wait` seconds (default 600) — the job ID is on stderr; poll it with the endpoints
below.
If the script errors, read it — it's plain `curl` + `jq` — and debug against `references/api.md`.
For array/struct parameters, destination tables or write disposition, `BATCH` priority, or
non-query jobs (load/extract/copy), use `jobs.insert` directly (next operation).
### 2. Submit a job directly (`jobs.insert` → `jobs.get`)
When you need a destination table, write disposition, `BATCH` priority, or a non-query job
(load/extract/copy), submit the job yourself and poll. `POST ${BQ}/jobs` returns immediately with a
`jobReference`:
```bash
JOB=$(bq_curl -X POST "${BQ}/jobs" -H "Content-Type: application/json" \
-d '{"configuration": {"query": {"query": "SELECT ...", "useLegacySql": false}}}')
JOB_ID=$(jq -r '.jobReference.jobId // empty' <<<"$JOB")
LOCATION=$(jq -r '.jobReference.location // empty' <<<"$JOB")
```
Then poll `GET ${BQ}/jobs/${JOB_ID}?location=${LOCATION}` until `.status.state` is `DONE` — sleep
between calls and bound the loop (`scripts/bq_query.sh` is the reference implementation). The full
`configuration.query` body — destination table, write disposition, `maximumBytesBilled`, priority,
parameters — is in `references/api.md`, section Query job configuration.
- If `JOB_ID` came back empty the insert itself failed; surface `.error` instead of polling.
- Always pass `location` back when polling — a job is pinned to the location it ran in, and
omitting it can 404 for non-US datasets.
- A `DONE` job can still have failed: check `.status.errorResult` before fetching results.
- Fetch a query job's rows with `GET ${BQ}/queries/${JOB_ID}?location=${LOCATION}&maxResults=1000` —
cells come back as `{"f": [{"v": "..."}]}` in schema order (column names in
`.schema.fields[].name`); more pages follow `.pageToken` (see Pagination).
### 3. Cancel a running job
```bash
bq_curl -X POST "${BQ}/jobs/${JOB_ID}/cancel?location=${LOCATION}" | jq '.job.status // .error'
```
Cancellation is best-effort; poll `jobs.get` to confirm.
### 4. List recent jobs
```bash
bq_curl "${BQ}/jobs?maxResults=20&projection=full&allUsers=false&stateFilter=done" \
| jq '.jobs[]? | {id: .jobReference.jobId, state: .status.state, query: (.configuration.query.query // "" | .[0:80]), bytes: .statistics.query.totalBytesProcessed}'
```
### 5. List datasets
```bash
bq_curl "${BQ}/datasets?maxResults=100" \
| jq '.datasets[]? | {id: .datasetReference.datasetId, location}'
```
Pass `?all=true` to include hidden datasets. For a different project's datasets, swap the project in the URL (you need `bigquery.datasets.get` there).
### 6. List tables in a dataset
```bash
DATASET="my_dataset"
bq_curl "${BQ}/datasets/${DATASET}/tables?maxResults=100" \
| jq '.tables[]? | {id: .tableReference.tableId, type, creationTime}'
```
`type` is `TABLE`, `VIEW`, `EXTERNAL`, `MATERIALIZED_VIEW`, or `SNAPSHOT`.
### 7. Get a table's schema and size
```bash
TABLE="events"
bq_curl "${BQ}/datasets/${DATASET}/tables/${TABLE}" \
| jq '{rows: .numRows, bytes: .numBytes, partitioning: .timePartitioning, schema: [.schema.fields[]? | {name, type, mode}]}'
```
Nested columns appear as `type: "RECORD"` with their own `fields[]` — recurse if you need the full tree.
### 8. Preview table rows without a query (`tabledata.list`)
Reads rows directly from storage — no query job, no bytes-scanned cost.
```bash
bq_curl "${BQ}/datasets/${DATASET}/tables/${TABLE}/data?maxResults=10" \
| jq '.rows[]?.f | map(.v)'
```
## Pagination
Every list-style endpoint uses the same scheme: the response carries a token when there's more, and you pass it back as `?pageToken=` on the next call. Stop when the field is absent. `maxResults` caps a single page; the actual API ceilings are size-based (~10 MB per `tabledata.list` page, ~20 MB per `getQueryResults` page) rather than a fixed row count — the bundled script defaults to 1000 rows per page.
The response field name is not uniform — check which one your endpoint returns:
- `pageToken` — `jobs.query`, `jobs.getQueryResults`, `tabledata.list`.
- `nextPageToken` — `datasets.list`, `tables.list`, `jobs.list`, `routines.list`, `models.list`.
Reading `.pageToken` on a `datasets.list` response silently yields `null` and you get one page.
Query results add one wrinkle: `jobs.query` and `jobs.getQueryResults` also return `totalRows`, which is the full count even when a single page is smaller — use it to size progress bars, not as a stop condition.
## Rate limits & quotas
BigQuery enforces per-project quotas rather than per-request rate limits. The ones you'll hit:
- **Concurrent queries**: BigQuery decides how many queries run at once (dynamic concurrency) and queues the rest — up to 1,000 queued interactive queries per project per region. Past that, submits fail with `quotaExceeded` / `jobRateLimitExceeded`.
- **API requests**: most methods are capped at ~100 requests per second per user per method (`jobs.get` and `tabledata.list` allow more) — poll with a `sleep`, not a hot loop.
- **On-demand bytes scanned** and **slot-time** quotas depend on your billing model.
`429` and `403 rateLimitExceeded` / `quotaExceeded` responses carry a retryable reason in `error.errors[].reason`. Back off exponentially and retry; don't tighten the poll interval.
## Error handling
Every error response is `{"error": {"code": N, "message": "...", "errors": [{"reason": "..."}]}}` — check `.error` before projecting. The `reason` string is the most useful field.
- `401` — Credential missing or rejected. Check `BQ_TOKEN` is set at all (any value works). If it persists, the credential isn't configured for this workspace — report it.
- `403 accessDenied` — Caller lacks permission on the project/dataset. Check which project is in the URL (billing project) vs. which project owns the data — they can differ. Grant `roles/bigquery.dataViewer` on the data, `roles/bigquery.jobUser` on the billing project.
- `404 notFound` — Job, dataset, or table doesn't exist, or you omitted `location` on a job lookup. Double-check the full reference (`project.dataset.table`). Always pass `?location=` on `jobs.get` / `getQueryResults`.
- `400 invalidQuery` — SQL error. The `message` carries line/column. Remember `useLegacySql: false`.
- job `status.errorResult` — Query ran but failed. A job can be `DONE` and still failed — always check `status.errorResult` before fetching results.
- `5xx` / `backendError` — Transient Google-side. Retry with backoff. Safe for read jobs; for write jobs, check whether the first attempt actually ran before retrying.
## Going deeper
`references/api.md` has the fuller endpoint catalog — the job configuration object in detail (destination tables, write disposition, load/extract/copy jobs), dataset and table create/update, routines, and row-level access policies. Read it when you need an endpoint not covered above or the exact body shape for a write.
Источник: anthropics/claude-tag-plugins / bigquery / bigquery-api ↗. Ссылка проверена 2026-10-10.