BigQuery Cost Audit
When to Use
- Reviewing BigQuery query costs, failure patterns, or performance inefficiencies.
- Identifying which jobs, users, or projects are driving the highest spend.
- Preparing optimization recommendations for an engineering or cost-review meeting.
- Auditing governance: scheduled jobs, duplicated logic, or low-value recurring queries.
Goals
- Identify the main cost drivers by job, project, and user.
- Detect repeated waste patterns (full scans, failed retries, duplicated logic).
- Suggest realistic optimizations with estimated impact.
- Translate technical waste into business-language findings.
What to Inspect
Cost hotspots
-- Top 20 most expensive jobs in the past 7 days
SELECT
job_id, user_email, query,
total_bytes_processed / POW(1024, 4) AS tb_processed,
ROUND(total_bytes_processed / POW(1024, 4) * 6.25, 2) AS estimated_cost_usd,
creation_time
FROM `region-us`.INFORMATION_SCHEMA.JOBS
WHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
AND job_type = 'QUERY'
AND state = 'DONE'
ORDER BY total_bytes_processed DESC
LIMIT 20;
Repeated failures
SELECT
error_result.reason, COUNT(*) AS failure_count, user_email,
ANY_VALUE(query) AS sample_query
FROM `region-us`.INFORMATION_SCHEMA.JOBS
WHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
AND error_result IS NOT NULL
GROUP BY error_result.reason, user_email
ORDER BY failure_count DESC;
Missing partition pruning
Look for queries that scan full tables despite available partition columns:
- No
WHERE filter on the partition column.
_PARTITIONTIME or _PARTITIONDATE not in the filter.
LIMIT used without a partition filter (does not reduce scan cost).
Missing clustering
Check high-scan queries that filter on non-clustered columns after partitioning is already in place.
Scheduled jobs with low value
-- Find scheduled queries with high scan volume (via Data Transfer Service run history)
-- Note: scheduled query metadata lives in region-specific transfer_run tables.
-- Substitute your project and region:
SELECT
config.display_name,
run.state,
run.end_time,
run.error_status
FROM `<project>.<region>.INFORMATION_SCHEMA.SCHEDULED_QUERY_RUNS` AS run
JOIN `<project>.<region>.INFORMATION_SCHEMA.SCHEDULED_QUERIES` AS config
ON run.scheduled_query_id = config.scheduled_query_id
WHERE run.end_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
ORDER BY config.display_name, run.end_time DESC;
-- Then cross-reference with JOBS to find per-run bytes_processed.
Output Format
- Top cost hotspots — job ID, user, bytes scanned, estimated USD, query snippet.
- Recurring failure patterns — error reason, count, user, sample query.
- Optimization opportunities:
- Partition pruning gaps.
- Clustering candidates.
- Queries eligible for materialization or caching.
- Scheduled jobs that should be retired or narrowed.
- Quick wins vs larger refactors — flag which optimizations are a one-line WHERE clause fix vs a schema change.
- Engineering summary — technical root causes and remediation steps.
- Business summary — cost impact in plain language; approximate monthly savings per opportunity.
Rules
- Focus on practical opportunities, not theoretical micro-optimizations.
- Prefer changes that reduce cost without increasing operational fragility.
- Do not run destructive operations.
- Do not edit code or queries unless explicitly asked.
- Acknowledge uncertainty when cost estimates depend on assumptions about query frequency.
Verification
1---2name: bigquery-cost-audit3description: Use when reviewing BigQuery spend, query failure patterns, or scan inefficiencies -- identifying which jobs, users, or projects drive cost, or preparing optimization recommendations for a cost review.4---56# BigQuery Cost Audit78## When to Use9- Reviewing BigQuery query costs, failure patterns, or performance inefficiencies.10- Identifying which jobs, users, or projects are driving the highest spend.11- Preparing optimization recommendations for an engineering or cost-review meeting.12- Auditing governance: scheduled jobs, duplicated logic, or low-value recurring queries.1314## Goals15- Identify the main cost drivers by job, project, and user.16- Detect repeated waste patterns (full scans, failed retries, duplicated logic).17- Suggest realistic optimizations with estimated impact.18- Translate technical waste into business-language findings.1920## What to Inspect2122### Cost hotspots23```sql24-- Top 20 most expensive jobs in the past 7 days25SELECT26 job_id, user_email, query,27 total_bytes_processed / POW(1024, 4) AS tb_processed,28 ROUND(total_bytes_processed / POW(1024, 4) * 6.25, 2) AS estimated_cost_usd,29 creation_time30FROM `region-us`.INFORMATION_SCHEMA.JOBS31WHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)32 AND job_type = 'QUERY'33 AND state = 'DONE'34ORDER BY total_bytes_processed DESC35LIMIT 20;36```3738### Repeated failures39```sql40SELECT41 error_result.reason, COUNT(*) AS failure_count, user_email,42 ANY_VALUE(query) AS sample_query43FROM `region-us`.INFORMATION_SCHEMA.JOBS44WHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)45 AND error_result IS NOT NULL46GROUP BY error_result.reason, user_email47ORDER BY failure_count DESC;48```4950### Missing partition pruning51Look for queries that scan full tables despite available partition columns:52- No `WHERE` filter on the partition column.53- `_PARTITIONTIME` or `_PARTITIONDATE` not in the filter.54- `LIMIT` used without a partition filter (does not reduce scan cost).5556### Missing clustering57Check high-scan queries that filter on non-clustered columns after partitioning is already in place.5859### Scheduled jobs with low value60```sql61-- Find scheduled queries with high scan volume (via Data Transfer Service run history)62-- Note: scheduled query metadata lives in region-specific transfer_run tables.63-- Substitute your project and region:64SELECT65 config.display_name,66 run.state,67 run.end_time,68 run.error_status69FROM `<project>.<region>.INFORMATION_SCHEMA.SCHEDULED_QUERY_RUNS` AS run70JOIN `<project>.<region>.INFORMATION_SCHEMA.SCHEDULED_QUERIES` AS config71 ON run.scheduled_query_id = config.scheduled_query_id72WHERE run.end_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)73ORDER BY config.display_name, run.end_time DESC;74-- Then cross-reference with JOBS to find per-run bytes_processed.75```7677## Output Format78791. **Top cost hotspots** — job ID, user, bytes scanned, estimated USD, query snippet.802. **Recurring failure patterns** — error reason, count, user, sample query.813. **Optimization opportunities**:82 - Partition pruning gaps.83 - Clustering candidates.84 - Queries eligible for materialization or caching.85 - Scheduled jobs that should be retired or narrowed.864. **Quick wins vs larger refactors** — flag which optimizations are a one-line WHERE clause fix vs a schema change.875. **Engineering summary** — technical root causes and remediation steps.886. **Business summary** — cost impact in plain language; approximate monthly savings per opportunity.8990## Rules91- Focus on practical opportunities, not theoretical micro-optimizations.92- Prefer changes that reduce cost without increasing operational fragility.93- Do not run destructive operations.94- Do not edit code or queries unless explicitly asked.95- Acknowledge uncertainty when cost estimates depend on assumptions about query frequency.9697## Verification9899- [ ] Hotspots listed with job, user, bytes scanned, and estimated USD100- [ ] Failure patterns grouped by error reason with counts and a sample query101- [ ] Each optimization classified as a quick win or a larger refactor102- [ ] Savings estimates state the assumptions behind them103- [ ] Findings include both an engineering summary and a business summary