
BigQueryAuthorCloudBrum editorial teamLast reviewedReading time~12 minLevelBeginner to advanced
Google Cloud · Data analytics · Serverless data warehouse
A data warehouse with no servers to size. BigQuery stores tables in its own columnar storage, runs SQL over them on Google’s machines, and can also query files that stay in Cloud Storage. It’s the closest Google equivalent to Amazon Athena when you use external tables, and a full warehouse when you load data in. Like Athena, it bills on-demand queries by the data they read, so partitioning, clustering, a column list and a maximum-bytes cap on each query are the habits that keep a bill sensible.
On this page (17 sections)
Mind map: BigQuery at a glance
Free to share. The downloaded image includes the CloudBrum link.
Key features (summary)
- Storage and compute kept apartData sits in BigQuery’s managed columnar storage, and queries run on a separate pool of compute that Google scales for you. You don’t provision clusters, and you don’t pay for compute while nothing runs.
- Two ways to pay for queriesOn-demand billing charges for the bytes a query reads, about $6.25 per TiB with the first TiB each month free. Capacity pricing reserves slots, the units of compute, in Standard, Enterprise and Enterprise Plus editions, billed by the slot-hour.
- Query files in Cloud StorageExternal tables and BigLake tables let SQL read Parquet, ORC, Avro, CSV or JSON files in a bucket without loading them. It’s the same idea as Athena, with the choice to load the data later for speed.
- Partitioning and clusteringPartition a table by a date or integer column, and cluster it by up to four more. Queries that filter on those columns read less, and cost less.
- Guard rails on spendA dry run reports the bytes a query would process before you run it. maximum_bytes_billed makes a query fail instead of exceeding a limit, and a table can require a partition filter on every query.
- Fine-grained accessIAM roles control datasets and tables. Policy tags protect columns, row-level access policies filter rows per user, and authorised views share a slice without exposing the base table.
- SQL that does machine learningBigQuery ML trains and runs models with SQL statements, and BI Engine caches data in memory to speed up dashboards.
- Streaming and scheduled workLoad files in batches for free, stream rows in for a charge, schedule queries and use Dataform or other tools for transformations.
Basic concepts
- Dataset
- A container for tables and views in one location. Access rules and default settings are often set at this level.
- Location
- The region or multi-region where a dataset and its data live. Queries and the data they read must be in the same location.
- Native table
- A table stored in BigQuery’s own columnar storage. It’s the fastest to query and the one that supports partitioning and clustering.
- External table
- A table definition over files in Cloud Storage or another source. The data stays where it is, and queries are generally slower than on native tables.
- BigLake table
- An external table with BigQuery security applied, including column and row rules, and access to storage through a connection rather than the user’s own rights.
- Partitioned table
- A table split by a time or integer column, so queries filtering on it read only the matching partitions.
- Clustered table
- A table whose storage is sorted by up to four columns, so filters and aggregations on them read fewer blocks.
- Slot
- A unit of BigQuery compute. On-demand queries use a shared pool, and editions let you reserve or autoscale slots.
- Edition
- A capacity pricing tier: Standard, Enterprise or Enterprise Plus, each with its own features and slot-hour price.
- Dry run
- A request that validates a query and returns the bytes it would process, without running it or billing you.
- Maximum bytes billed
- A per-query setting that makes the query fail if it would process more than the limit.
- Policy tag
- A label from a taxonomy that restricts who can read a column. It’s the basis for column-level security.
- Materialized view
- A precomputed result that BigQuery keeps up to date, so repeated aggregations read less data.
- INFORMATION_SCHEMA
- Built-in views with metadata, including JOBS, which lists every query with bytes processed and the user who ran it.
How to create & configure it
Query files and a native table with bq
- Pick the project, the location and the layoutUse a project with billing, and choose a dataset location near your data, such as asia-south1 for Mumbai. Keep the bucket and the dataset in the same location, because BigQuery can’t query across locations.
- Store files as Parquet in foldersWrite files under paths such as orders/order_date=2026-10-28/. BigQuery can read that layout as Hive partitions, which lets filters on order_date skip folders.
- Create the datasetRun bq mk --dataset with the location. Give it a short description and, if your rules need it, a default encryption key.
- Create an external tableGenerate a table definition with bq mkdef, using Hive partitioning, and create the external table from it. Now you can query the files straight away.
- Dry-run, then run with a capRun the query with --dry_run to see the bytes it would process. Then run it with --maximum_bytes_billed, so a mistake fails instead of costing money.
- Load a native table for the hot dataUse CREATE TABLE AS SELECT to copy the data into a partitioned, clustered native table, with require_partition_filter turned on so every query has to name a date range.
- Share it safelyGrant roles/bigquery.jobUser on the project, and roles/bigquery.dataViewer on the dataset, to the groups that should query. Use views or policy tags for sensitive columns.
- Watch the spendQuery INFORMATION_SCHEMA.JOBS for the top queries by bytes processed, and set project or user quotas as a backstop.
- Decide on capacityIf on-demand spend is large and steady, compare it with an edition and a slot reservation or autoscaling for a month.
Tip: always run the dry run before a query you haven’t run before. It’s free, and it tells you the cost of the question before you ask it. See the code references for the same setup as Terraform.
Common issues after configuration
| Symptom | Likely cause | What to check / fix |
|---|---|---|
| Not found: Dataset ... was not found in location ... | The query or the table is in a different location from the dataset or the bucket | Create the dataset in the same location as the bucket, and set the job location explicitly with --location. |
| Access Denied: ... bigquery.jobs.create | The caller can read the data but has no right to run queries in the billing project | Grant roles/bigquery.jobUser on the project that runs the query, and roles/bigquery.dataViewer on the dataset. |
| Query exceeded limit for bytes billed | The query would read more than maximum_bytes_billed | Add a partition filter, select fewer columns, or raise the limit on purpose after a dry run. |
| Cannot query over table without a filter over column(s) ... that can prune partitions | The table has require_partition_filter set, and the query has no usable filter | Add a filter on the partition column with a constant or a simple expression, for example a date range. |
| External table returns no rows or Error while reading table | The URI pattern doesn’t match, the files have a different schema, or the connection can’t read the bucket | Check the wildcard and the Hive prefix, align the column types, and grant the service account or connection roles/storage.objectViewer. |
| A SELECT * on a table costs far more than expected | BigQuery bills by columns read, and LIMIT doesn’t reduce what is scanned | Name the columns you need, and filter on partitions. Use a table preview or TABLESAMPLE to look at data cheaply. |
| Queries are slow on an external table | Reading many small files over the network is slower than reading native storage | Load frequently used data into a native partitioned table, or use BigLake with metadata caching. |
| Resources exceeded during query execution | A single operation, such as a huge sort or a skewed join, doesn’t fit in the memory a slot has | Filter earlier, avoid global ORDER BY on large results, aggregate before joining, and check for a very frequent join key. |
| Exceeded rate limits or quotaExceeded | Too many concurrent queries, load jobs or streaming rows for the project | Spread the work, batch the loads, and consider reservations for predictable concurrency. |
| Streaming inserts show up late in queries | Recent rows sit in a streaming buffer before they’re fully available | Query with that delay in mind, or use the Storage Write API and check its guarantees for your case. |
| The bill jumped after a dashboard went live | A dashboard re-runs a wide query on every refresh, or many users hit it | Point it at a materialized view or a summary table, turn on BI Engine or caching, and review jobs in INFORMATION_SCHEMA.JOBS. |
Use cases: when and where to use it
Good fit
- Analytics over large tables where you don’t want to run or size a warehouse
- Dashboards and reports in Looker, Looker Studio or other BI tools
- Querying logs and exports in Cloud Storage with external tables first and loading later
- Joining data from many sources, including SaaS exports, in one place
- Machine learning on tabular data using SQL with BigQuery ML
- Event and log analysis with streaming inserts and partitioned tables
Consider something else
- Application transactions and row updates by key → a database such as Cloud SQL
- Large code-based transformations → Dataflow or Dataproc with Spark
- Very small data or a single-user analysis → a spreadsheet or a local database
- Real-time lookups with millisecond latency → Bigtable, Spanner or an in-memory cache
- The same job on other clouds → compare Amazon Athena and Azure Synapse Analytics
Pros: why consider it?
Why teams choose it
- No infrastructure to size, patch or scale
- Strong cost controls: dry run, maximum bytes billed and required partition filters
- Fast on native tables, with partitioning, clustering and materialized views
- Queries Cloud Storage files too, through external and BigLake tables
- Fine-grained security with policy tags and row-level rules
- SQL-based machine learning, and many connectors and BI tools that already speak to it
Trade-offs to weigh
- On-demand cost follows bytes read, so wide, unfiltered queries are expensive
- Not built for frequent single-row updates or low-latency lookups
- Queries and data must share a location, and moving data between locations needs a copy
- External tables are slower and less featured than native tables
- Slot-based pricing needs capacity planning to pay off
- Streaming and some features have separate charges and quirks
Common threats, vulnerabilities & risks
| Risk | Severity | Why it matters | Mitigation |
|---|---|---|---|
| A wide query over a big table with no limits | High | A single SELECT * over years of data can process many TiB and be billed for all of it. | Require partition filters, set maximum bytes billed in tools and quotas per project and user, and dry-run unknown queries. |
| Datasets shared too widely | High | Broad roles such as bigquery.admin or allAuthenticatedUsers expose all data. | Grant roles to groups at dataset level, use authorised views and review access. |
| Sensitive columns without protection | High | Personal or financial columns are visible to anyone who can read the table. | Use policy tags, row-level security and data masking, and keep sensitive tables in a restricted dataset. |
| Service account keys for BI and jobs | Medium | Long-lived keys leak through code and files. | Use workload identity or attached service accounts, and block key creation by policy. |
| External tables over unmanaged buckets | Medium | Anyone who can change the files changes the query results, and schema drift breaks queries. | Control bucket write access, version the layout and prefer BigLake tables with managed access. |
| Dashboards that rescan on every refresh | Medium | Cost grows with the number of users and refreshes, not with data size. | Use materialized views, BI Engine and summary tables. |
| Data in the wrong location | Medium | Rules on where data may live can be broken, and cross-location queries fail. | Choose the location at dataset creation and enforce it by organisation policy. |
| No retention rules | Low–Med | Old and scratch tables sit in storage and bill every month. | Set default table and partition expiry on datasets. |
| Reservations bought without a usage check | Low–Med | Committed slots that sit idle cost more than on-demand. | Compare real usage with on-demand cost, and start with autoscaling. |
Performance optimisation
- Partition large tables by the date you filter on, and cluster by the columns that appear in filters and joins, most selective first.
- Name columns and avoid SELECT *. BigQuery stores data by column, so unread columns cost nothing.
- Filter early, and put filters on the partition and clustering columns using plain comparisons.
- Use materialized views or summary tables for repeated aggregations, and BI Engine for dashboards that need instant response.
- Denormalise with nested and repeated fields where it fits, which avoids large joins.
- Avoid skewed joins and global sorts. Aggregate before joining and use LIMIT with ORDER BY only on small results.
- Load external data into native tables when you query it often, since native storage is faster.
- Look at the query execution graph in the console for stages that are slow or spilling.
Reduce monthly cost
FinOps is the habit of managing cloud spend together with engineering. These are the FinOps checks that matter most for BigQuery.
- On-demand queries bill by bytes processed, about $6.25 per TiB with the first TiB each month free. Columns, partitions and clustering are your levers.
- Turn on require partition filter for big tables, and set maximum bytes billed in scripts and tools.
- Run a dry run before unfamiliar queries, and teach analysts to read the bytes estimate.
- Use expiration on scratch tables and partitions, and let old data move to cheaper long-term storage pricing automatically.
- Compare on-demand with an edition for steady, heavy workloads, and start with autoscaling before committing to slots.
- Use materialized views and summary tables so dashboards read small tables.
- Prefer batch loads, which are free, to streaming inserts when seconds of delay are acceptable.
- Set custom quotas per project and per user as a backstop against runaway queries.
- Export billing data and query INFORMATION_SCHEMA.JOBS to find the most expensive queries and users.
- Label datasets and jobs by team, and set budget alerts.
The prices here are Google Cloud list prices as quoted by secondary sources in October 2026 (about $6.25 per TiB on-demand, with the first TiB each month free), and they differ by location and change over time. Check the live pricing page before you plan around them (see Pricing).
Security optimisation
- Grant IAM roles to groups at the dataset level, with bigquery.jobUser on the billing project and dataViewer on the data only.
- Protect sensitive columns with policy tags and use row-level access policies where users should see different rows.
- Share slices of data through authorised views and authorised datasets, not by copying tables.
- Use customer-managed encryption keys if your rules need them, and set a default key for the dataset.
- Put BigQuery inside a VPC Service Controls perimeter to limit data exfiltration, and use Private Google Access.
- Use service accounts without keys for jobs and BI tools, and block service account key creation with an organisation policy.
- Enable Cloud Audit Logs for data access, and export them to a central project and to BigQuery for analysis.
- Choose the dataset location to meet data residency rules, and enforce it with an organisation policy.
- Control the buckets behind external and BigLake tables: write access to the files is write access to your results.
- Review access with IAM recommender and the INFORMATION_SCHEMA views, and remove unused roles.
Governance & compliance
Guardrails
- Labels on datasets and jobs (owner, environment, data class, cost centre) enforced through Terraform modules
- An organisation policy that restricts BigQuery locations and blocks allUsers and allAuthenticatedUsers bindings
- A shared Terraform module for datasets with an expiry default, an owner group and a required partition filter on large tables
- A catalogue entry (Dataplex or another tool) and a steward for every table that holds personal or financial data
Continuous checks (Security Command Center and organisation policy)
- The organisation policy constraint for allowed resource locations (gcp.resourceLocations)
- A Security Command Center finding for datasets that are publicly accessible
- A Security Command Center finding for datasets without customer-managed keys where required
- A scheduled query on INFORMATION_SCHEMA.JOBS that flags queries above a bytes threshold
- A check on policy tag coverage for columns that match sensitive names
Cloud Audit Logs record who ran queries and changed datasets, INFORMATION_SCHEMA shows every job with its cost, Security Command Center reports findings, Dataplex can catalogue and classify data, and the Compliance resource center holds Google’s certificates. Google runs the storage and the query engine, while you own the data, its layout, access rules, locations and the habits that decide the bill.
Reduce carbon footprint
- Read less: partitions, clustering and named columns cut the work for each query.
- Use summary tables and materialized views, so dashboards don’t recompute the same result.
- Set expiry on scratch data, so storage doesn’t grow without a reason.
- Choose a location with a high carbon-free energy share when data rules allow. Google publishes the percentage per region.
- Prefer batch loads and scheduled queries at flexible times over constant streaming where it isn’t needed.
- Compress and export only what you need, and avoid copying data between locations.
- Cloud Carbon Footprint in the console estimates emissions per project and service. Check what it shows for BigQuery before you quote a figure.
Pricing
Purchase options
On-demand queries Per TiB processed
About $6.25 per TiB of data processed in the main US location according to the sources we read, with the first 1 TiB each month free, and a 10 MB minimum for each table a query reads.
Editions (capacity) Per slot-hour
Standard, Enterprise and Enterprise Plus reserve or autoscale slots, billed by the slot-hour. Sources quote about $0.04, $0.06 and $0.10 per slot-hour in the US.
Storage Per GiB per month
Active storage for data changed in the last 90 days, and a lower rate for long-term storage. The first 10 GiB each month are free.
Everything around it Varies
Streaming inserts and the Storage Write API, queries of external data and Cloud Storage reads, BI Engine capacity, BigQuery ML training and data transfer.
What you actually pay for
- Bytes processed by on-demand queries, or slot-hours for an edition
- Active and long-term storage of tables
- Streaming writes and some export or load paths
- Cloud Storage and network costs for external tables
- BI Engine, BigQuery ML and other add-ons you turn on
Rates differ by location and change over time, and the figures above come from secondary sources. See the official BigQuery pricing page, the Google Cloud Pricing Calculator and Google's guide on controlling BigQuery costs.
Important code references
Order data stored as Parquet in Cloud Storage, partitioned by day. The bq tab creates a dataset, an external table over the files, runs a dry run and a capped query for the last seven days, then copies the data into a partitioned, clustered native table that requires a partition filter. Terraform builds the dataset, both tables and access for an analyst group. The Python tab estimates the cost with a dry run before it runs a query, and the C# tab runs a parameterised query with a byte cap. Replace the project, bucket and group with your own. Queries cost by the data they read, so start with a dry run.
# Placeholders: replace the project, bucket and group with your own.
PROJECT=my-orders-project
LOCATION=asia-south1
BUCKET=orders-lake-example
ANALYSTS=analysts@example.com
gcloud config set project $PROJECT
gcloud services enable bigquery.googleapis.com
# Who may do what: run jobs in the project, read the dataset.
gcloud projects add-iam-policy-binding $PROJECT --member="group:$ANALYSTS" --role=roles/bigquery.jobUser
# The dataset sits in the same location as the bucket. Scratch tables expire after 30 days.
bq --location=$LOCATION mk --dataset --description "Order analytics" --default_table_expiration 2592000 $PROJECT:orders_lake
# An external table over the Parquet files in Cloud Storage; folders named order_date=YYYY-MM-DD become a partition column.
bq mkdef --source_format=PARQUET --hive_partitioning_mode=AUTO \
--hive_partitioning_source_uri_prefix=gs://$BUCKET/orders/ "gs://$BUCKET/orders/*" > orders_def.json
bq --location=$LOCATION mk --table --external_table_definition=orders_def.json $PROJECT:orders_lake.orders_ext
# Dry run first: how many bytes would the last seven days read?
SQL="SELECT order_date, COUNT(*) AS orders, ROUND(SUM(total), 2) AS revenue
FROM \`$PROJECT.orders_lake.orders_ext\`
WHERE order_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY) AND status = 'PAID'
GROUP BY order_date ORDER BY order_date"
bq --location=$LOCATION query --use_legacy_sql=false --dry_run "$SQL"
# Run it, but fail instead of reading more than 10 GiB.
bq --location=$LOCATION query --use_legacy_sql=false --maximum_bytes_billed=10737418240 "$SQL"
# Copy the data into a native table. Every query on it has to name a date range.
bq --location=$LOCATION query --use_legacy_sql=false "
CREATE TABLE \`$PROJECT.orders_lake.orders\`
PARTITION BY order_date
CLUSTER BY status, customer_id
OPTIONS (require_partition_filter = TRUE)
AS SELECT order_id, customer_id, total, currency, status, CAST(order_date AS DATE) AS order_date
FROM \`$PROJECT.orders_lake.orders_ext\`"
# The five queries that processed the most data in the last day.
bq --location=$LOCATION query --use_legacy_sql=false "
SELECT user_email, job_id, ROUND(total_bytes_processed / POW(1024, 3), 1) AS gib_processed
FROM \`region-$LOCATION\`.INFORMATION_SCHEMA.JOBS
WHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY) AND job_type = 'QUERY'
ORDER BY total_bytes_processed DESC LIMIT 5"
# Placeholders: pass in your project, bucket and analyst group.
variable "project_id" {
type = string
}
variable "location" {
type = string
default = "asia-south1"
}
variable "lake_bucket" {
type = string
default = "orders-lake-example"
}
variable "analysts_group" {
type = string
default = "analysts@example.com"
}
resource "google_bigquery_dataset" "orders_lake" {
dataset_id = "orders_lake"
location = var.location
description = "Order analytics"
default_table_expiration_ms = 2592000000
delete_contents_on_destroy = false
}
# Files stay in Cloud Storage; folders named order_date=YYYY-MM-DD become a partition column.
resource "google_bigquery_table" "orders_ext" {
dataset_id = google_bigquery_dataset.orders_lake.dataset_id
table_id = "orders_ext"
deletion_protection = true
external_data_configuration {
source_format = "PARQUET"
autodetect = true
source_uris = ["gs://${var.lake_bucket}/orders/*"]
hive_partitioning_options {
mode = "AUTO"
source_uri_prefix = "gs://${var.lake_bucket}/orders/"
}
}
}
# The hot copy: partitioned by day, clustered, and every query must name a date range.
resource "google_bigquery_table" "orders" {
dataset_id = google_bigquery_dataset.orders_lake.dataset_id
table_id = "orders"
deletion_protection = true
require_partition_filter = true
clustering = ["status", "customer_id"]
time_partitioning {
type = "DAY"
field = "order_date"
}
schema = jsonencode([
{ name = "order_id", type = "STRING", mode = "REQUIRED" },
{ name = "customer_id", type = "STRING", mode = "NULLABLE" },
{ name = "total", type = "FLOAT", mode = "NULLABLE" },
{ name = "currency", type = "STRING", mode = "NULLABLE" },
{ name = "status", type = "STRING", mode = "NULLABLE" },
{ name = "order_date", type = "DATE", mode = "REQUIRED" },
])
}
# Analysts may run queries in the project and read this dataset, nothing more.
resource "google_project_iam_member" "analysts_jobs" {
project = var.project_id
role = "roles/bigquery.jobUser"
member = "group:${var.analysts_group}"
}
resource "google_bigquery_dataset_iam_member" "analysts_read" {
dataset_id = google_bigquery_dataset.orders_lake.dataset_id
role = "roles/bigquery.dataViewer"
member = "group:${var.analysts_group}"
}
# Estimates a query with a dry run, then runs it with a cap on the bytes billed.
# pip install google-cloud-bigquery (uses Application Default Credentials)
import sys
from google.cloud import bigquery
client = bigquery.Client(location="asia-south1")
RATE_PER_TIB = 6.25 # USD, the on-demand list price at the time of writing; the first TiB each month is free
SQL = """
SELECT order_date, COUNT(*) AS orders, ROUND(SUM(total), 2) AS revenue
FROM `my-orders-project.orders_lake.orders`
WHERE order_date >= DATE_SUB(CURRENT_DATE(), INTERVAL @days DAY) AND status = @status
GROUP BY order_date ORDER BY order_date
"""
def estimate_and_run(days=7, status="PAID", cap_gib=10):
params = [bigquery.ScalarQueryParameter("days", "INT64", days), bigquery.ScalarQueryParameter("status", "STRING", status)]
dry = client.query(SQL, job_config=bigquery.QueryJobConfig(query_parameters=params, dry_run=True, use_query_cache=False))
gib = dry.total_bytes_processed / 1024 ** 3
print("this query would process %.3f GiB, about $%.4f at the list rate" % (gib, gib / 1024 * RATE_PER_TIB))
if gib > cap_gib:
raise SystemExit("more than %d GiB, not running it" % cap_gib)
config = bigquery.QueryJobConfig(query_parameters=params, maximum_bytes_billed=cap_gib * 1024 ** 3)
job = client.query(SQL, job_config=config)
for row in job.result():
print(row.order_date, row.orders, row.revenue)
print("billed: %.3f GiB (cache hit: %s)" % ((job.total_bytes_billed or 0) / 1024 ** 3, job.cache_hit))
if __name__ == "__main__":
estimate_and_run(days=int(sys.argv[1]) if len(sys.argv) > 1 else 7)
// dotnet add package Google.Cloud.BigQuery.V2
// Runs a parameterised query and refuses to bill more than 10 GiB for it.
// Uses Application Default Credentials (gcloud auth application-default login, or the service account on the machine).
using Google.Cloud.BigQuery.V2;
string project = args.Length > 0 ? args[0] : "my-orders-project";
BigQueryClient client = await BigQueryClient.CreateAsync(project);
const string sql = @"
SELECT order_date, COUNT(*) AS orders, ROUND(SUM(total), 2) AS revenue
FROM `my-orders-project.orders_lake.orders`
WHERE order_date >= DATE_SUB(CURRENT_DATE(), INTERVAL @days DAY) AND status = @status
GROUP BY order_date ORDER BY order_date";
var parameters = new[]
{
new BigQueryParameter("days", BigQueryDbType.Int64, 7),
new BigQueryParameter("status", BigQueryDbType.String, "PAID")
};
var options = new QueryOptions { MaximumBytesBilled = 10L * 1024 * 1024 * 1024 };
BigQueryJob job = await client.CreateQueryJobAsync(sql, parameters, options);
job = await job.PollUntilCompletedAsync();
job.ThrowOnAnyError();
BigQueryResults results = await client.GetQueryResultsAsync(job.Reference);
foreach (BigQueryRow row in results)
Console.WriteLine($"{row["order_date"]} orders={row["orders"],5} revenue={row["revenue"],12:N2}");
Console.WriteLine($"Billed {(job.Statistics.Query.TotalBytesBilled ?? 0) / 1_073_741_824.0:F3} GiB.");
Best videos to explore
Short walkthrough videos for this service are being prepared. They will appear here and load only when you press play. Until then, follow CloudBrum on YouTube for new videos.
Interview questions
BasicWhat is BigQuery?
It’s Google Cloud’s serverless data warehouse. You store tables in BigQuery or query files in Cloud Storage, write SQL, and Google runs it on its own infrastructure with no servers for you to size or patch. You pay for the data your queries read, or for reserved capacity.
BasicHow is BigQuery priced?
On-demand queries cost about $6.25 per TiB of data processed, with the first TiB each month free. Capacity pricing in Standard, Enterprise and Enterprise Plus editions charges per slot-hour instead. Storage is billed per GiB a month, at a lower rate for tables not changed for 90 days, with the first 10 GiB free. Check Google’s pricing page for current rates.
BasicDoes LIMIT reduce the cost of a BigQuery query?
No. BigQuery bills for the columns it reads, and a LIMIT doesn’t reduce that. To cut cost, name only the columns you need and filter on the partition and clustering columns. A dry run shows the bytes a query would process before you run it.
IntermediateHow do partitioning and clustering lower the cost of a query?
Partitioning splits a table by a date or integer column, so a filter on it reads only the matching partitions. Clustering sorts storage by up to four columns, so filters and aggregations on them skip blocks. Both reduce the bytes processed, and therefore the bill, and speed up the query.
IntermediateWhat is the difference between an external table and a native table?
A native table is stored in BigQuery’s own columnar storage and is the fastest, with full support for partitioning, clustering and caching. An external table points at files in Cloud Storage that stay where they are. It’s handy for exploring and for data other tools share, but queries are slower, so load the data you query often into native tables.
IntermediateHow do you stop a query from running up a huge bill?
Run a dry run to see the bytes first, set maximum_bytes_billed on the job so it fails above a limit, require a partition filter on large tables, and set project and user quotas as a backstop. Review INFORMATION_SCHEMA.JOBS regularly to find expensive queries and who ran them.
AdvancedWhen would you move from on-demand pricing to an edition with slots?
When monthly on-demand spend is large and steady enough that reserved or autoscaled slots cost less, or when you need predictable cost and workload isolation. Compare a month of real usage, start with autoscaling in a reservation rather than a long commitment, and use separate reservations for separate teams if their workloads shouldn’t compete.
AdvancedHow would you give different users different views of the same table?
Use row-level access policies for rows, policy tags for sensitive columns, and authorised views when you want to expose a slice or an aggregate without granting access to the base table. Keep the base tables in a restricted dataset and give analysts only the views.
AdvancedHow would you design a lake and warehouse split with BigQuery?
Keep raw files in Cloud Storage as Parquet or Iceberg and expose them with BigLake tables for governed access. Load the curated, frequently used data into partitioned and clustered native tables, build materialized views and summary tables for dashboards, and apply policy tags and VPC Service Controls across both.
AdvancedWhy might a query on an external table be slow, and what would you do?
Reading many small files over the network, with no partition pruning and no column statistics from the engine, is slower than reading native storage. Use Hive partition layouts, larger Parquet files and metadata caching with BigLake, or load the hot data into a native table.
Similar or alternative services on other clouds
| Capability | AWS | Azure | Google Cloud |
|---|---|---|---|
| Service | Amazon Athena for files, Amazon Redshift as the warehouse | Azure Synapse Analytics, with Microsoft Fabric for new projects | BigQuery |
| Query files in object storage | Athena tables over S3 | Serverless SQL pool over the lake | External and BigLake tables |
| Own storage for tables | Redshift managed storage | Dedicated SQL pool storage | Native BigQuery storage |
| On-demand price | About $5 per TB scanned | About $5 per TB processed | About $6.25 per TiB, first TiB a month free |
| Reserved compute | Athena capacity or Redshift clusters | Dedicated SQL pools | Editions with slot reservations |
| Per-query cost guard | Workgroup scan limit | Data-processed limits per day, week and month | Maximum bytes billed, required partition filter |
| Column-level security | Lake Formation | Column-level security and masking in SQL | Policy tags and data masking |
| Built-in machine learning | Redshift ML, SageMaker | Synapse ML and Fabric notebooks | BigQuery ML |
If you only need ad hoc SQL over files, external tables are the nearest match to Amazon Athena. For application data and transactions use a database such as Cloud SQL. The files themselves live in Cloud Storage, and big code-based transformations belong in Dataflow or Dataproc.
Did you find this article helpful?
Your answer helps us improve this page for the next reader.
Sources and further reading
CloudBrum wrote this page in its own words, using the provider’s public documentation listed below as the reference. Features, limits, names and prices change, so confirm anything that matters on the provider’s own pages before you decide.
- What is BigQuery?Google Cloud
- BigQuery pricingGoogle Cloud
- Estimate and control costsGoogle Cloud
- Introduction to partitioned tablesGoogle Cloud
- Introduction to clustered tablesGoogle Cloud
- Introduction to external tablesGoogle Cloud
- Introduction to BigLake tablesGoogle Cloud
- Introduction to column-level access controlGoogle Cloud
- Terraform Google provider: google_bigquery_tableHashiCorp
