Optimizing BigQuery Queries
SkillDatabases & dataReduce Google BigQuery cost and runtime, partitioning and clustering, minimizing bytes processed, avoiding SELECT * and full scans, slot usage and reservations, approximate functions, and materialized views. Use when BigQuery queries are expensive or slow, bytes billed are high, a query scans full tables, or you need to size slots.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the Optimizing BigQuery Queries skill
What this skill tells your AI
The instructions your AI receives, as published by unknown-333/awesome-data-engineering-skills in skills/optimizing-bigquery-queries/SKILL.md and read by ahel’s review.
When to use
- BigQuery queries cost too much (bytes billed) or run slowly.
- A query scans full tables or ignores partitions.
- Choosing partitioning/clustering, or sizing slots/reservations.
- Do NOT use for query logic correctness (this assumes correct results).
Workflow
- [ ] Estimate bytes: query validator or --dry_run BEFORE running
- [ ] Partition by date/timestamp; cluster by most-filtered columns
- [ ] Select only needed columns; filter on the partition column
- [ ] Replace exact-distinct/full scans with approx / incremental
- [ ] Materialize repeated aggregates
- Estimate first. On-demand billing = bytes processed. Use the editor's
validator or
bq query --dry_runto see bytes billed before spending. - Partition + cluster. Partition large tables by date/timestamp; cluster by the columns you filter/join on most. Filtering on the partition column prunes scanned bytes dramatically.
- Read fewer columns. BigQuery is columnar —
SELECT *reads every column's bytes. List only what you need. - Avoid full scans. Filter on the partition column with literals/ranges (not wrapped in functions) so pruning applies.
- Approximate + materialize. Use
APPROX_COUNT_DISTINCTfor big cardinality; use materialized views for common aggregates.
Patterns
Partitioned + clustered table:
CREATE TABLE sales.orders
PARTITION BY DATE(ordered_at)
CLUSTER BY customer_id, status AS
SELECT ...;
Prune-friendly filter (keeps the partition column bare):
-- Good: prunes partitions
WHERE ordered_at >= '2026-01-01' AND ordered_at < '2026-02-01'
-- Bad: function on the column disables pruning
WHERE DATE(ordered_at) = '2026-01-15'
Dry run to see cost:
bq query --use_legacy_sql=false --dry_run 'SELECT ... FROM sales.orders WHERE ...'
Slots: on-demand gives per-query slots with fair scheduling; buy reservations/editions for predictable heavy workloads and isolate ELT from BI with separate reservations.
Common pitfalls
SELECT *— reads all columns' bytes; the most common cost mistake.- Function-wrapped partition filter (
DATE(ts) = ...) — disables pruning and scans the whole table. - No partitioning on large tables — every query full-scans.
COUNT(DISTINCT ...)on huge columns — expensive; useAPPROX_COUNT_DISTINCTwhen exactness isn't required.- Re-running the same heavy aggregate — cache with a materialized view or a scheduled summary table.
- Ignoring the dry-run estimate — surprise bills; always estimate first.
- Cross-joins / unintentional fan-out — explode bytes and slot time; check the execution graph.
Signals
- GitHub stars
- 21
- Last commit
- Aug 2026
Advanced
- Item type
- skill
- Key
optimizing-bigquery-queries- Source
- github.com/unknown-333/awesome-data-engineering-skills