Optimizing BigQuery Queries

SkillDatabases & data

Reduce 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.

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
  1. Estimate first. On-demand billing = bytes processed. Use the editor's validator or bq query --dry_run to see bytes billed before spending.
  2. 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.
  3. Read fewer columns. BigQuery is columnar — SELECT * reads every column's bytes. List only what you need.
  4. Avoid full scans. Filter on the partition column with literals/ranges (not wrapped in functions) so pruning applies.
  5. Approximate + materialize. Use APPROX_COUNT_DISTINCT for 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; use APPROX_COUNT_DISTINCT when 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