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.
What it does
Gives the model Google's playbook for optimizing BigQuery. It rewrites SQL, proposes table layouts, and explains billing choices, while telling it to hand cost-affecting changes to you instead of running them.
How it works
- SQL optimization: a list of concrete rules (range filters instead of
EXTRACT, null-safeNOT IN,LIMITwithORDER BY, no top-levelSELECT *, pre-aggregating before joins, and more), always ending with a summary of what changed. - Table design: partitioning vs. clustering, DDL templates, migrating an unpartitioned table, and
require_partition_filter. - Storage and billing: logical vs. physical storage, time travel, partition expiration, and when search indexes, materialized views or BI Engine make sense.
- Capacity: how to read Slot Estimator recommendations for On-Demand vs. Editions. Commitments, reservations and billing-model changes are left to you in the Cloud Console.
Good for
Tuning an expensive query, designing or fixing a large table, and reviewing a BigQuery bill.
Limits
The SQL part works from the query text alone. The analysis parts need the Google Cloud SDK and access to your project; it is not a replacement for checking the dry-run numbers.
Medium risk: no scripts, but the instructions tell the agent to run `gcloud` and `bq` against your Google Cloud project. That needs the Google Cloud SDK, a signed-in account (or a service-account key file), billing, and IAM roles (it mentions BigQuery admin/data editor/job user). The setup steps include `gcloud config set project` and `gcloud services enable`, which change your project settings, and a companion-skill install command (`npx skills add ...`). The skill says to tag its own `gcloud` calls with a usage-attribution label and add a skill name and version to the User-Agent sent to Google; you can ask to turn that off. It tells the agent not to purchase commitments, change billing models or run DDL itself, and to propose those for you to run. Even so, review each command before approving it, use a non-production project first, and remember that queries and storage changes cost money. In our trial only the SQL rewrite path was used, with no cloud access.