Scope: project shop-prod, region us. You didn't give a region, so I assumed us. If shop lives elsewhere, use your region in any INFORMATION_SCHEMA queries (for example region-europe-west1). I haven't run anything against your project. This rewrite comes from the query text alone, and I'm assuming order_ts is a TIMESTAMP.
Optimized query
SELECT
order_id, -- replace with the columns you actually need
customer_id,
order_ts
FROM
`shop-prod.shop.orders`
WHERE
order_ts >= TIMESTAMP '2025-01-01 00:00:00'
AND order_ts < TIMESTAMP '2026-01-01 00:00:00'
AND customer_id NOT IN (
SELECT customer_id
FROM `shop-prod.shop.blocked`
WHERE customer_id IS NOT NULL
)
ORDER BY
order_ts
LIMIT 1000; -- remove or adjust if you really need every row
Summary of optimizations
- Replaced
EXTRACT(YEAR FROM order_ts) = 2025with a range filter. A function on the column prevents partition pruning and block pruning. A plain range onorder_tslets BigQuery prune statically. - Added
WHERE customer_id IS NOT NULLto theNOT INsubquery. This lets BigQuery use an anti-hash join instead of the slower left outer hash join.- Behavior change: if
shop.blockedcontains a NULLcustomer_id, the original query returns zero rows, becauseNOT INwith a NULL evaluates to NULL. The rewrite returns the rows you almost certainly intended. If you somehow relied on the old behavior, tell me.
- Behavior change: if
- Replaced
SELECT *with an explicit column list. This reads less data. I don't know your schema, so fill in the columns you need. - Added
LIMITto theORDER BY. A full sort of a year of orders is expensive. If you need all rows, drop theLIMIT, or consider whether the sort is needed at all.
Table layout (biggest lever if the table is large)
The range filter only prunes partitions if shop.orders is partitioned on order_ts. To check, run:
bq show --format=prettyjson shop-prod:shop.orders
Look at timePartitioning, clustering and requirePartitionFilter in the output.
If the table is not partitioned, and each day holds roughly 10 GB or more (otherwise cluster only), you can create a partitioned copy:
CREATE TABLE `shop-prod.shop.orders_partitioned`
PARTITION BY TIMESTAMP_TRUNC(order_ts, DAY)
CLUSTER BY customer_id
OPTIONS (require_partition_filter = TRUE)
AS
SELECT * FROM `shop-prod.shop.orders`;
You can't convert the existing table in place. Note that require_partition_filter = TRUE makes any query without an order_ts filter fail.
Validate before running
A dry run shows the bytes the query would process, and it costs nothing:
bq query --use_legacy_sql=false --dry_run "<the optimized query>"
Run it on the original query and on the rewrite, and compare totalBytesProcessed. The dry run won't show savings from clustering, since those happen at execution time.
These changes should improve cost-efficiency, but I can't promise a particular saving until you've compared the dry-run numbers.
Trial with only the Skill and Read tools: no Google Cloud access, no gcloud or bq. So only the SQL-rewrite path was exercised, from the query text alone; the model proposed the bq show and dry-run commands but did not run them. Capacity, billing and gcloud workflows were read but not tried.
它做什么
把 Google 优化 BigQuery 的做法交给模型:改写 SQL、给出表结构方案、解释计费选择;涉及花钱的改动则要求交给你自己执行,而不是模型代跑。
工作方式
- SQL 优化:一份具体规则清单(用范围条件代替
EXTRACT、对NOT IN子查询排除空值、ORDER BY配LIMIT、避免最外层SELECT *、先聚合再连接等),并在最后列出改了什么。 - 表设计:分区与聚簇怎么选、DDL 模板、把未分区的表迁移成分区表、
require_partition_filter。 - 存储与计费:逻辑存储与物理存储、时间旅行、分区过期,以及什么时候该用搜索索引、物化视图或 BI Engine。
- 容量:怎样看 Slot Estimator 对按需计费与 Editions 的建议。购买承诺、改预留和改计费模式一律交给你在 Cloud Console 里操作。
适合
优化昂贵的查询、设计或修复大表、复盘 BigQuery 账单。
局限
SQL 改写只靠查询文本就能做;分析类的部分需要 Google Cloud SDK 和你项目的访问权限,也不能代替你去看 dry run 的数字。
中风险:没有脚本,但指令会让智能体对你的 Google Cloud 项目运行 `gcloud` 和 `bq`。这需要安装 Google Cloud SDK、已登录的账号(或服务账号密钥文件)、结算账号和 IAM 角色(文中提到 BigQuery 管理员、数据编辑者、作业用户)。准备步骤里有 `gcloud config set project` 和 `gcloud services enable`,会改变项目设置,还有一条安装配套 Skill 的命令(`npx skills add ...`)。Skill 还要求给它自己发出的 `gcloud` 调用打上用量归因标记,并在发给 Google 的 User-Agent 里加上 Skill 名称和版本,你可以要求关闭。它明确要求智能体不要自己购买承诺、修改计费模式或执行 DDL,而是提出方案由你来执行。即便如此,每条命令都请先看清再批准,先在非生产项目里试用,并记住查询和存储变更都会产生费用。我们的试用只走了 SQL 改写这条路径,没有连接云端。