Q1
GCP Data Engineering · Chapter 03
Building a Data Warehouse in BigQuery
Cloud Storage and BigQuery are the two services you will use in almost every later chapter. This chapter builds a small warehouse layer by layer, and spends real time on the hardest part: modelling.
0 of – checks answered
01
Cloud Storage and BigQuery
Two fully managed services with no infrastructure to size. Look at what each one is, where the data lives, and what BigQuery is not.
Object storage for any file, any size
A fully managed service: no pre-sizing, no nodes, no bandwidth planning. Data engineers use it for database dumps, exports of historical data, model files and as the staging area before a warehouse load.
- Model
- Objects inside buckets
- Bucket names
- Globally unique
- Typical role
- Lake and staging layer
- Limit to know
- Single objects have a maximum size (a quota)
The four warehouse blocks, hidden from you
BigQuery has the same four parts as any warehouse: storage, processing, metadata and a SQL interface. Storage is a distributed columnar file system (Colossus) and processing is a distributed SQL engine inspired by Google’s internal Dremel. You only ever see tables and SQL.
- Storage
- Columnar, on Google’s distributed file system
- Processing
- Distributed SQL engine
- You touch
- Tables (metadata) and SQL
- Why care
- Knowing this helps when tuning or troubleshooting
Where the bytes physically live
Datasets are stored and processed in the region or multi-region you choose. Not every service exists in every region, so check availability. A load from Cloud Storage into BigQuery needs the bucket and the dataset in the same location.
- You choose
- A region or multi-region per dataset and bucket
- Gotcha
- Bucket and dataset location must match for loads
- Why it matters
- Data-locality rules and cost
- Practice tip
- Use one location for the whole exercise
A warehouse, not an application database
BigQuery excels at scanning huge tables for analysis and can ingest streams and run machine learning in SQL. It is not a transactional database and is not built for millisecond lookups: a terabyte in seconds does not mean a megabyte in microseconds.
- Great at
- Analytics over large tables
- Also does
- Streaming inserts and ML in SQL
- Poor at
- Row-level, low-latency transactions
- Use instead
- Cloud SQL, Spanner, Bigtable, Firestore
02
A warehouse, layer by layer
Data does not jump from source to dashboard. It passes through layers, and each layer has one job. Step through the book's bike-sharing pipeline.
The application database
In the book’s scenario a Cloud SQL (MySQL) database holds the stations table. In real life you extract from a replica, so the production database is not disturbed.
- Example
- MySQL on Cloud SQL
- Job here
- Be extracted from, not queried heavily
Files in Cloud Storage
The extract lands as files in a bucket, in date-named folders such as one per day. Cloud Storage scales to any volume, variety and velocity, and this pattern works for almost any source.
- Example
- gs://bucket/mysql_export/stations/20180101/
- Job here
- The E of ELT, kept in a standard shape
Loaded as-is into BigQuery
Files are loaded into a raw dataset with an explicit schema. Nothing is reshaped yet, so the raw layer can always be rebuilt. Business users are usually not given access here.
- Example
- raw_bikesharing.trips
- Job here
- The L of ELT
Modelled facts and dimensions
This is where modelling happens: cleaned, consistent tables shaped around business questions, for example a daily fact table and a stations dimension.
- Example
- dwh_bikesharing.fact_trips_daily and dim_stations
- Job here
- The T of ELT, and the single source of truth
Answers for one audience
Views or tables named after a team, built on the warehouse, that answer that team’s questions. Business users query here, and can export results (for example to CSV).
- Example
- dm_operational.bike_trips_daily
- Job here
- Serve people, hide complexity
Notice the naming: raw_, dwh_ and dm_ prefixes make a dataset's role obvious. Datasets should also be
named for business meaning, such as customer loans or website logs, not technical purposes such as layer 1.
03
Loading data safely
How you write into a table depends on whether the data is an event or a snapshot. Get this wrong and you get duplicates or lost rows.
Event or snapshot?
-
01 A customer taps a bike out of a dock at 08:03.
-
02 A docking station has a name, a region and a capacity.
-
03 A sensor reports 21.4 °C at 12:00:05.
Pick the write mode
-
01 Each day new bike trips arrive. A trip never changes once it has happened.
-
02 A stations file arrives daily as a full copy. Stations can be added, changed or removed, and you only need the current picture.
-
03 A one-off load must never overwrite a table that already has data.
04
Views or tables?
Both can serve a data mart. The right choice depends on data size and how often the result is used.
| View | Table (physicalised) | |
|---|---|---|
| Extra storage | None, only the SQL is saved | Stores a copy of the result |
| Freshness | Always current | As fresh as its last refresh |
| Cost of each read | Re-runs the query every time | Reads stored rows |
| Best when | Source is small or the query is light | Heavy joins or aggregations reused many times |
-
01 A small stations table, and a manager wants an always-fresh top-two-regions answer without storing anything extra.
-
02 1,000 dashboards each use a view that joins and aggregates five multi-petabyte tables.
-
03 You want zero extra storage and results that update the moment the source table does.
05
Data modelling
A data model represents the business in tables people can understand without asking you. Good models balance four things that pull against each other: real-world fit, consistency, query speed and storage.
Normalised vs denormalised: try it
A gender label stored as text on every person row is denormalised. Storing a small id and looking the label up in a reference table is normalised. Flip between them and see what each buys you.
- Gender column stores
- Rename "Woman" to "Female"
- Reading the label
- Risk
Storage is cheap in BigQuery and joins are far less scary than in the Hadoop era, so consistency and clarity usually win. But a model with no thought at all leaves you with duplicated data, conflicting numbers and users who no longer trust the warehouse.
Inmon or Kimball?
| Inmon | Kimball | |
|---|---|---|
| Direction | Top-down: central warehouse first, then marts | Bottom-up: start from user questions |
| Shape | Highly normalised | Fact and dimension tables (star schema) |
| Strength | Consistency, single source of truth | Speed to value, ease of use, query performance |
| Weakness | Slow to change and to build | Some inconsistency and duplication |
-
01 Finance needs statements that cannot contain mistakes, from sources that rarely change, and accuracy matters more than build speed.
-
02 New sources and columns appear every week and the business wants answers quickly, even if some inefficiency remains.
-
03 You are building one enterprise-wide source of truth first, with every data mart obeying its rules.
The book's answer to "which is best for BigQuery" is both, depending on the use case. Whichever you pick, the whole organisation commits to it for a long time, so experienced modellers usually decide.
Facts and dimensions
A fact table holds measurements at a fixed granularity, such as trips per station per day. A dimension table holds the attributes that describe an entity, such as a station's name and region. In a star schema a dimension has no parent tables, so it is denormalised.
-
01 Total trips and total trip seconds per station per day.
-
02 Each station’s name, region and capacity.
-
03 A customer’s name, segment and signup date.
BigQuery also supports nested and repeated columns: store each region's stations inside the region row. You keep denormalised convenience and avoid repeating region fields, though SQL users new to nested data may find it awkward.
06
Hands-on: two scenarios
Do these in order. Part A gets you comfortable; part B builds the warehouse. Your ticks are saved in this browser.
Part A: get comfortable
-
Open each service once in the console so its API is enabled for your project.
-
Find Explorer, the query editor and the job and query history panels.
SELECT "hello world"; -
Use Create table, source Upload, and let BigQuery auto-detect the schema for a headed CSV. Then delete both when done.
-
Add the project
bigquery-public-databy name, openbaseball.schedules, and read its size under Details before querying. Costs scale with bytes scanned.SELECT * FROM `bigquery-public-data.baseball.schedules` LIMIT 1000; -
A project-name prefix keeps it unique. Use the same location you will use for your datasets.
-
Clone the book’s GitHub repository in Cloud Shell and copy the chapter 3 dataset into the bucket. The book uses
gsutil; Google now recommendsgcloud storagefor the same job.gcloud storage cp -r Data-Engineering-with-Google-Cloud-Platform/* gs://YOUR_BUCKET/from-git/
Part B: build the bike-sharing warehouse
-
This costs money by the hour. Use the smallest tier, set your own strong root password, and delete the instance as soon as you have exported the table.
-
Give the Cloud SQL service account permission to write objects to the bucket first (an IAM role; Chapter 9 explains why).
-
The export has no header row, so do not auto-detect. Type the schema yourself.
station_id:STRING,name:STRING,region_id:STRING,capacity:INTEGER -
Top two regions by total station capacity, then save the result as CSV.
SELECT region_id, SUM(capacity) AS total_capacity FROM `PROJECT.raw_bikesharing.stations` WHERE region_id != '' GROUP BY region_id ORDER BY total_capacity DESC LIMIT 2; -
Loop over a list of dataset names (raw, warehouse, marts) and create any that do not exist. Doing it in code is what makes it repeatable.
-
Then prove there are two dates and no duplicate trip IDs.
SELECT DISTINCT DATE(start_date) FROM `PROJECT.raw_bikesharing.trips`; SELECT trip_id, COUNT(*) c FROM `PROJECT.raw_bikesharing.trips` GROUP BY trip_id HAVING c > 1; -
Write the result of a SELECT on the public table into a table in your raw dataset.
-
Group by station_id and confirm nothing appears twice.
-
A daily fact table joined to a stations dimension. Run the fact build once per date.
-
Daily trips, daily average duration, top five start stations by duration, and the three regions with the shortest total duration.
-
Delete the Cloud SQL instance and any datasets and buckets you no longer need.
gcloud sql instances delete mysql-instance-source
For the final self-check, add a question the model cannot yet answer (for example, top three regions by female riders on the latest date) and decide which fact and dimension tables you need. There is no single correct model.
07
Key terms
- Bucket / object
- A Cloud Storage container with a globally unique name, and the files inside it.
- Dataset
- A named group of BigQuery tables and views.
- Colossus
- Google’s distributed file system that holds BigQuery storage.
- Staging layer
- Files landed in Cloud Storage before loading.
- Raw / warehouse / mart
- The three dataset layers: as-loaded, modelled, and per-audience.
- Event vs snapshot
- Something that happened (append-only) vs something that exists (can change).
- Write disposition
- A load setting: append, truncate or write-if-empty.
- Fact table
- Measurements at a defined granularity.
- Dimension table
- Attributes describing an entity that supports facts.
- Star schema
- A fact table surrounded by denormalised dimensions.
- Normalised / denormalised
- Split into related tables to avoid repetition, or kept together for ease of use.
08
Check yourself
Pick an answer to see the reasoning straight away.
Q2
A load from a bucket in one region fails against a dataset in another. What is the most likely rule you broke?
Q3
Why can BigQuery keep timestamps only in UTC, and what does that force on you?
Q4
Which best describes a BigQuery dataset?
Q5
In BigQuery, which trade-off does the book argue you can lean toward?
Q6
What does BigQuery’s nested and repeated data offer?
Study notes in my own words, following the structure of Adi Widjaja's Data Engineering with Google Cloud Platform (Packt, 2022). GCP changes quickly, so check each service's current documentation before relying on names, prices or console steps.