Storing the data · Topic 2 of 4

Designing a BigQuery Data Warehouse

BigQuery warehouse design: star schemas, nested and repeated fields, how far to normalize, mapping business needs, partitioning and clustering for access.

  • 5 min
In a nutshell
  • Define the grain first, then measures and dimensions.
  • Star schemas are fine; nested and repeated fields remove costly one-to-many joins.
  • Denormalize for large, read-together data; keep often-changing shared dimensions separate.

What it is

Planning a warehouse means choosing a data model that answers the business questions, deciding how far to normalize, and shaping tables so the common queries read as little data as possible. In BigQuery, storage is cheap and scans cost money or slots, so the design favours fewer joins and pruned scans.

Business questionsGrain and metricsModelstar, nested, widePhysical designpartition, clusterAccess layerviews and marts

How it works

Mapping business requirements

Start with the questions, not the source tables.

AskDesign decision
What is one row? (the grain)Fact table at order, order line, event or daily snapshot level
Which measures?Numeric columns in the fact table: revenue, quantity, duration
Sliced by what?Dimensions: customer, product, date, region
Is history needed?Slowly changing dimension type 2 (valid from and valid to columns) or periodic snapshots
How fresh?Batch loads, micro-batches or streaming ingestion
Who uses it?Data marts or authorized views per audience

Designing the data model

ModelShapeFits BigQuery when
Star schemaFact table plus dimension tablesBI tools expect it; dimensions are shared and change independently
Snowflake schemaDimensions normalized furtherRarely worth it; adds joins
Denormalized wide tableDimension attributes copied into the factVery large facts, simple fixed reporting
Nested and repeated fieldsSTRUCT and ARRAY columns inside one rowOne-to-many data read together, such as an order and its lines
CREATE TABLE sales.orders (
  order_id STRING,
  order_ts TIMESTAMP,
  customer_id STRING,
  items ARRAY<STRUCT<sku STRING, qty INT64, price NUMERIC>>
)
PARTITION BY DATE(order_ts)
CLUSTER BY customer_id
OPTIONS (require_partition_filter = TRUE);

-- revenue per SKU for one week, without a join
SELECT i.sku, SUM(i.qty * i.price) AS revenue
FROM sales.orders, UNNEST(items) AS i
WHERE DATE(order_ts) BETWEEN '2026-10-01' AND '2026-10-07'
GROUP BY i.sku;

Deciding the degree of normalization

Choose more normalization whenChoose more denormalization when
Dimension attributes change often and must be updated in one placeTables are huge and the same joins run constantly
The dimension is shared by many factsData is read together and changes rarely
Storage duplication would be very largeQuery speed and simplicity matter most

A common compromise is a star schema with nested child data in the fact. Small dimension tables join cheaply, and nesting removes the most expensive one-to-many join.

Exam tip

Exam answers that "normalize to third normal form" for an analytics warehouse are usually wrong. BigQuery performs best with denormalized or nested data and a few small joins.

Defining architecture to support access patterns

Access patternFeature
Filters on a date or timestampPartition by that column (time-unit column partitioning)
Rows arrive without a usable date columnIngestion-time partitioning
Filters on an integer ID rangeInteger-range partitioning
Filters or joins on high-cardinality columns such as customer IDCluster by up to four columns
Users forget the date filterrequire_partition_filter = TRUE
The same aggregation runs constantlyMaterialized view
Point lookups of text values in large tablesSearch index
Different audiencesLayered datasets (raw, curated, marts) and authorized views

Prefer one partitioned table over many date-sharded tables such as events_20261016. Partitioned tables have one schema, less metadata overhead and simpler queries.

A worked example

A subscription business reports revenue by day, plan and country, and support staff look up one customer's history. Customers change plans, and finance needs the plan as it was at the time of each invoice.

The engineer builds an invoice fact table partitioned by invoice date and clustered by customer ID. A customer dimension uses type 2 history so each invoice joins to the version valid on its date. Invoice lines are nested in the fact to avoid a large join.

InvoicesFactpartition by date, cluster by customerJoincustomer version on dateMartsfinance and support

How the exam asks about this

  • "Queries always filter on event_date but scan the whole table." Partition the table by event_date and require a partition filter.
  • "Orders and their line items are always read together and the join is slow." Store line items as a nested repeated field in the orders table.
  • "Queries filter by customer_id within a date range on a 50 TB table." Partition by date and cluster by customer_id.

What to remember for the exam

  • Define the grain first, then measures and dimensions.
  • Star schemas are fine; nested and repeated fields remove costly one-to-many joins.
  • Denormalize for large, read-together data; keep often-changing shared dimensions separate.
  • Partition by the date filter; cluster by high-cardinality filter columns, up to four.
  • Prefer partitioned tables to date-sharded tables.
Watch out

Clustering alone does not guarantee lower cost the way partition pruning does; its savings show only after a query runs. A dry run estimate reflects partition pruning but not clustering.

Common mistakes

Fully normalizing an analytics model. Many joins add slot time and complexity for no benefit in BigQuery.

Partitioning by a column nobody filters on. Pruning only helps when queries filter on the partitioning column.

Creating one table per day. Sharded tables are harder to query and manage than one partitioned table.

Check yourself

  • How do you store an order and its line items in one BigQuery row?

    Use a repeated STRUCT column (an ARRAY of STRUCTs) for the line items and read it with UNNEST.

  • How many columns can a BigQuery table be clustered by?

    Up to four columns, in order of filter importance.

  • When should a dimension stay normalized in its own table?

    When its attributes change often or it is shared by many fact tables, so updates happen in one place.

Topic 2 of 4 in Storing the data

Test yourself on Storing the data

The full Google Cloud Professional Data Engineer question bank has 733 questions, with timed practice exams and an explanation for every answer.

This study guide is independent and is not affiliated with, endorsed by, or sponsored by Google Cloud. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.