Skip to content
Finitizer

BigQuery Query Cost Analyzer

Paste a BigQuery query to see what makes it expensive, or compare its bytes before and after an optimization.

Google CloudReads your own dataA few numbers
New here? See what this tool does, how to use it and how to get your data.

Learn more

BigQuery Query Cost Analyzer

What this free tool does for you

Catch an expensive BigQuery query before it goes on a schedule, see what drives its cost, and measure what an optimization saved from the bytes before and after.

How to use it

  1. Pick a mode: Structure only (findings, no price), I have the bytes, Add my schema or Compare before / after.
  2. Paste your SQL under Your query, or click Upload .sql. Try an example loads a sample.
  3. With I have the bytes, enter the Amount BigQuery says the query will process, the Runs per month and the Region.
  4. With Add my schema, click Copy SQL, run it in BigQuery, and paste the result under Your table metadata.
  5. With Compare before / after, no SQL is needed: enter Bytes before, Bytes after and the Runs per month.

Good to know

  • Where to find the bytes. Before a run, the console estimate or bq query --dry_run. After it, Bytes billed in the job details.
  • LIMIT rarely saves money. BigQuery bills the data a query reads, not the number of rows it returns.
  • Private. Your SQL is analyzed in your browser, and sent only if you choose to share it for a review.

Not sure where to get the data? .

Your query

Your SQL is analyzed in this browser tab. It is never sent unless you choose Share for a review, and nothing is written to the URL.

Prices verified 23 Oct 2026. Public list prices in USD for US multi-region, before any discount you have negotiated.

Want this done on your real bill?

BigQuery Cost Audit: We find the queries, tables and slots driving your BigQuery bill and fix them without slowing anyone down. From $2,500, fixed. Nothing paid upfront.

See the service

Why there are four modes

BigQuery’s own cost estimate comes from a dry run, and a dry run needs credentials. This page holds no credentials and never sees your data, which means it cannot produce that number by itself. Rather than assume a table size and present the result as a calculation, it separates what it can know from what it cannot:

  • Structure only, findings and ratios from the SQL alone. Deliberately no dollar figure.
  • I have the bytes, you supply the dry-run figure and it is priced at real regional rates. The figure is BigQuery’s estimate before the query runs, not the bill.
  • Add my schema, an INFORMATION_SCHEMA extract lets us model bytes per table: fixed-width columns from their documented widths, variable-width columns calibrated against the table’s real logical size rather than guessed. Still a model, not a dry run.
  • Compare before / after, the bytes of a run before and after a change, from job history or two dry runs, priced at the same rate. No SQL, and nothing modeled: one observed pair already carries whatever partitioning and clustering saved.

Clustering is never modeled, because block pruning cannot be predicted before a query runs, even a dry run returns an upper bound on a clustered table. Where a partition filter cannot be turned into a partition count, the estimate assumes a full scan and says so, which overstates cost rather than flattering it.

What it looks for

11 detectable anti-patterns, each with its own page explaining the mechanism and the fix.

What SELECT * actually costs in BigQuery
BigQuery bills the columns you read, not the rows you return. So SELECT * is the most expensive way to write any query.
LIMIT does not reduce BigQuery query cost
Adding LIMIT 10 to a query changes what you see, not what you are billed. It is the most common cost misconception in BigQuery.
Querying a partitioned table without a partition filter
A partitioned table with no filter on its partition column scans every partition. That is usually the single largest avoidable cost in a BigQuery estate.
A partition filter that looks like pruning but is not
Filtering the partition column against a subquery or a joined column does not prune anything. BigQuery has to read every partition to evaluate it.
Wildcard tables without a _TABLE_SUFFIX filter
A wildcard table pattern with no _TABLE_SUFFIX constraint reads every matching table, which for daily-sharded data means the entire history.
A CTE referenced more than once may be evaluated more than once
BigQuery does not promise to compute a WITH clause once. A CTE used in three places may read its tables three times; check with a dry run.
Self-joins where a window function would do
Joining a table to itself may read it twice; check with a dry run. A window function reads it once and is usually faster as well.
CROSS JOIN and joins with no equality condition
A join without an equality condition produces the Cartesian product. It rarely ends in a bill, it usually ends in a failed query after burning a lot of slot time.
The 10 MB minimum, multiplied by many small tables
BigQuery bills at least 10 MB per table referenced per query. Join forty lookup tables and you have bought 400 MB before reading any real data.
ORDER BY without LIMIT on a large result
A top-level sort with no LIMIT forces the whole result through a single worker. It is the most common cause of "resources exceeded".
COUNT(DISTINCT) where an approximation would do
Exact distinct counts require a full shuffle. APPROX_COUNT_DISTINCT is typically within 1% and dramatically cheaper in slot time.
What a SELECT * view costs in BigQuery
A SELECT * view bills only the columns each query takes from it. It costs extra when readers select every column, as BI tools often do.
The same query, over and over, uncached
Repetition is where BigQuery bills add up. A $2 query on a 15-minute schedule is $5,760 a month.
Rewriting a table resets it to active storage pricing
Data untouched for 90 days drops to roughly half price automatically. A write resets the clock: for the whole table, or for one partition of a partitioned table.

Questions

How do I estimate the cost of a BigQuery query before running it?

The closest answer before running it is a dry run: paste the query into the BigQuery console and read the validator message, or run bq query --dry_run. That returns BigQuery's estimate of the bytes processed; the bytes a completed run bills can differ, and on a clustered table they are often lower. This tool prices that figure, and separately finds the structural reasons the number is as large as it is, which the dry run does not tell you.

Why does this tool not just tell me the cost from my SQL?

Because it cannot honestly. Bytes processed depend on how much data sits in the columns your query touches, which is not in the SQL. Any tool that prints a dollar figure from SQL alone has assumed a table size, and that assumption is doing all the work. Give it your schema for a modeled estimate, or your dry-run figure to price BigQuery's own estimate.

How do I measure what an optimization saved?

Run the query before and after the change on the same data range and parameters, without the cache, and read Bytes billed for each job (or total_bytes_billed in INFORMATION_SCHEMA.JOBS). Enter both under Compare before / after: no SQL is needed. It prices them at the on-demand rate for your region, or a rate you enter, times the runs a month. One observed before and after already includes what partitioning and clustering each did, so they are not added separately. With Editions it shows the change in bytes only, because capacity bills slot time.

Does LIMIT reduce BigQuery cost?

No, not on a non-clustered table. LIMIT truncates the result after BigQuery has read the data it needs, so bytes billed are identical with and without it. To look at a sample cheaply, use table preview or bq head, which read no billable bytes.

How much does SELECT * cost in BigQuery?

It costs the total logical size of every column in the table, per run, regardless of how many rows you get back. On a wide table where you need four columns out of two hundred, that is roughly fifty times what the query needs to cost.

Why is my partition filter not reducing the bytes scanned?

Pruning happens at planning time, so the filter value has to be resolvable before any data is read. Literals and functions of literals qualify. A value coming from a subquery, a join, or a correlated reference does not, so every partition is read and the filter is applied afterward. Resolve the value into a scripting variable or a query parameter first. Two more ways to lose pruning: a partition filter on one side of an OR, and the column wrapped in a function Google does not list, such as FORMAT_DATE. DATE(ts) and TIMESTAMP_TRUNC(ts, DAY) still prune.

Is it safe to paste my SQL here?

Yes. The analysis runs entirely in your browser: your SQL is never sent unless you choose Share for a review, and no part of your query is written into the URL. The site records anonymous usage events, and the /privacy page lists exactly what those contain.

Related tools

More tools for: Analytics and warehouses

One query at a time is a slow way to find them all.

Finitizer ranks every query in your project by cost, groups them by shape so you see the repeated ones, and rewrites the expensive ones with an AI optimizer that shows the cheaper version side by side. Read-only and keyless.

See how Finitizer works

Rate this tool

How would you rate the BigQuery Query Cost Analyzer?