This is your companion book for Weeks 1–4 — written for someone switching
careers into data, not someone who already codes. Every tool is explained before you're asked to use
it, every command is shown in full, and every example uses the same running case study so the ideas
connect instead of floating in isolation.
4
Weeks covered
6
Tool spotlights
1
Running case study
3
Portfolio projects
00
BEFORE YOU START
How This Book Works
Three ideas that make everything after this page easier to follow.
Read → Run → Break → Explain
You are not expected to memorize syntax. You are expected to type every example yourself, run it, and then break it on purpose to see what the error looks like. That last step matters more than people think — a data engineer's real job is reading error messages calmly and figuring out what they mean.
1. Code Along
Don't just read the code blocks — retype them in your own editor. Typing builds muscle memory that reading never will.
2. Break It on Purpose
Misspell a column name. Remove a comma. Read the error. This is how you stop being afraid of red text.
3. Explain It Back
After each lesson, say out loud (or write one sentence) what the code does. If you can't, re-read the lesson.
Meet BrightCart: Your Case Study
Every code example, every SQL query, and every mini-project in this book — across all three phases — uses the same small dataset: a fictional online store called BrightCart. Real data engineering work is never a random toy example per lesson; you follow one system end to end. This book does the same thing on purpose.
BrightCart sells electronics, home goods, apparel, and books. Its data has exactly the shape you'll meet in a real job: customers who sign up, products with prices, orders that get placed, and order line items — plus some orders that are still pending, cancelled, or refunded, because real businesses aren't tidy.
The BrightCart schema — one customer places many orders, one order can contain many order_items, and every order_item points at one product.
Table
Grain (one row =)
Used to teach
customers
one person who signed up
filtering, joins, dimension tables
products
one item BrightCart sells
joins, categories, pricing logic
orders
one checkout event
dates, status, aggregation
order_items
one product inside one order
joins, SUM/quantity math, fact tables
Why a fake store and not a "real" public dataset?
Public datasets are messy in ways that distract from the lesson at hand. BrightCart is small enough to hold in your head (16 customers, 14 products, 45 orders) but shaped exactly like production data, including a deliberately messy version you'll clean in Phase 2. You always know what the "right" answer should look like, which is how you learn to spot when your own query is wrong.
Download the Case Study Files
These files are provided alongside this book. Save them into one project folder — you'll load the .sql files into PostgreSQL in Chapter 5, and read the .csv files with Python starting in Chapter 2.
Files in this bundle
01_schema.sql
02_seed_clean_data.sql
customers.csv
products.csv
orders.csv
order_items.csv
order_totals_clean.csv
dirty_orders.csv (Phase 2)
Keep one project folder
Create a folder called de-bootcamp and put a data/ subfolder inside it for these files. Chapter 1 walks you through creating exactly this structure with Git tracking it from day one.
01
WEEK 1 · THE WORKBENCH
Setting Up Your Developer Environment
Four tools you'll open every single day of this bootcamp. Install them once, understand them properly, and everything after this chapter goes faster.
Why Start With Tools, Not Code?
A carpenter doesn't start a project by learning to swing a hammer mid-job — they set up the workbench first. Data engineers do the same. Before you write a line of Python or SQL, you need a place to write it (an editor), a way to run it (an interpreter), a way to talk to your computer directly (a terminal), and a way to save your progress so nothing is ever lost (version control).
Python 3
The language interpreter — turns the code you write into instructions your computer executes.
VS Code
The IDE (editor) — where you write, run, and debug every script and query in this book.
The Terminal
The command line — how you navigate folders and run programs without clicking icons.
Git & GitHub
Version control — saves checkpoints of your work and gives you a public portfolio.
Tool Spotlight: Python
15 min
Tool Spotlight
Python 3
The programming language you'll use for almost everything in this bootcamp.
What it is
Python is an interpreter: a program that reads your .py file line by line and executes it immediately. There's no separate "compile" step, which is why it's a popular first language.
Why data engineers use it
Nearly every data tool you'll touch later — pandas, Airflow, PySpark — is a Python library. Learning Python fundamentals now is what makes all of Phase 2 and 3 possible.
How to get it running
1
Go to python.org/downloads and download the latest Python 3 installer for your OS.
2
Windows: during install, tick "Add python.exe to PATH" before clicking Install — this single checkbox prevents 90% of beginner setup issues.
3
Mac: the installer works out of the box; Terminal.app already knows where Python lives once installed.
4
Restart your terminal, then verify the install (see "Try it" below).
Try it
terminal
$ python --version
Python 3.12.4
$ python3 --version # Mac/Linux sometimes need python3, not python
Python 3.12.4
Common snag: If python --version says "command not found," the PATH checkbox above was likely missed. Re-run the installer and choose "Modify" to fix it — no need to uninstall first.
Tool Spotlight: VS Code
10 min
Tool Spotlight
Visual Studio Code (VS Code)
A free code editor with the exact features a data engineer needs — nothing more, nothing less.
What it is
VS Code is a text editor built for writing code: it colors your syntax, catches typos before you run anything, and has a built-in terminal panel so you never have to alt-tab away from your code.
Why data engineers use it
You'll live inside VS Code for all three phases — writing Python scripts, SQL files, Airflow DAGs, and Dockerfiles all in one window, with Git built directly into the sidebar.
How to get it running
1
Download it from code.visualstudio.com and run the installer (default options are fine).
2
Open VS Code, click the Extensions icon in the left sidebar (four squares icon).
3
Install the Python extension (by Microsoft) — enables run/debug and autocomplete.
4
Install SQLTools (optional but recommended) — lets you run SQL queries against PostgreSQL from inside VS Code later in this chapter.
Try it
terminal
# From a terminal, inside any project folder:
$ code .
# The dot means "open the current folder in VS Code"
Common snag: If code . doesn't work, open VS Code manually once, press Cmd/Ctrl+Shift+P, type "Shell Command: Install 'code' command in PATH", and select it.
Tool Spotlight: The Terminal
15 min
Tool Spotlight
The Terminal (Command Line)
A text-based way to control your computer — no mouse required.
What it is
The terminal is a program that accepts typed commands instead of clicks: moving between folders, running scripts, and installing tools all happen here. On Windows it's usually PowerShell or the "Terminal" app; on Mac it's Terminal.app; VS Code has one built in under the menu Terminal → New Terminal.
Why data engineers use it
Data engineers live in the terminal because most real tools — Git, Docker, cloud CLIs, Airflow — are only fully controllable from the command line. There is no "skip this" option later in the bootcamp, so it's worth getting comfortable now.
How to get it running
1
You already have one — open VS Code and go to Terminal → New Terminal (or press Ctrl+`).
2
Confirm you're in your home folder by running pwd (Mac/Linux) or cd with no arguments (Windows) — it prints your current location.
3
Practice moving around using the commands in the table below before continuing.
Command
What it does
Example
pwd
Print Working Directory — shows where you are
pwd
cd
Change Directory — move into a folder
cd Desktop
cd ..
Move up one folder
cd ..
ls / dir
List files in the current folder (Mac/Linux vs Windows)
ls
mkdir
Make a new folder
mkdir de-bootcamp
python file.py
Run a Python script
python hello.py
terminal — moving into a new project folder
$ pwd
/Users/ana/Desktop
$ mkdir de-bootcamp
$ cd de-bootcamp
$ pwd
/Users/ana/Desktop/de-bootcamp
$ ls
# (empty — nothing in here yet)
Beginner trap: typing paths with the wrong slashes
Mac/Linux use forward slashes (data/raw); Windows PowerShell also accepts forward slashes in most commands, so when in doubt, use / — it works almost everywhere in this book.
Tool Spotlight: Git & GitHub
20 min
Tool Spotlight
Git & GitHub
Git is your project's time machine. GitHub is where that history becomes a public portfolio.
What it is
Git is a program installed on your computer that tracks every change you make to a project folder, in checkpoints called commits. GitHub is a website that stores a copy of that history online, so employers, teammates, and future-you can see it.
Why data engineers use it
Every mini-project and portfolio project in this bootcamp gets committed to Git and pushed to GitHub. Your GitHub profile is your resume as a career shifter — it's proof you can actually build things, not just say you can.
How to get it running
1
Download Git from git-scm.com/downloads and install with default options.
2
Verify it worked: run git --version in your terminal.
3
Create a free account at github.com if you don't already have one.
4
Tell Git who you are (one-time setup, shown in "Try it" below) — every commit gets stamped with this name and email.
Try it
terminal — one-time setup
$ git --version
git version 2.44.0
$ git config --global user.name "Ana Dela Cruz"
$ git config --global user.email "ana.delacruz@example.com"
Common snag: Don't skip the git config step — without it, your first commit will fail with an "author identity unknown" error.
How Git Actually Works
Git moves your work through three places. Understanding this diagram now will save you real confusion later — almost every Git error a beginner hits comes from not knowing which of these three states their file is in.
A file moves left to right: you edit it, you `add` it to staging, you `commit` it to local history, you `push` it to GitHub.
Term
Meaning
Repository (repo)
A project folder that Git is tracking.
Commit
A saved checkpoint, with a short message describing what changed.
Stage / git add
Marking which changed files should go into the next commit.
Push
Uploading your local commits to GitHub.
Clone
Downloading a copy of a GitHub repo onto your computer.
the five commands you'll type more than any others in this bootcamp
git init # start tracking this folder
git status # what changed since the last commit?
git add . # stage everything that changed
git commit -m "first commit" # save a checkpoint
git push # upload to GitHub
A daily habit, not a one-time event
Run git add . && git commit -m "..." every time you finish a small, working piece of code — not just at the end of the day. Small commits with clear messages are what a real engineering team (and a hiring manager skimming your GitHub) expects to see.
Activity: Your First Repository
15 min
Hands-on Activity 1
Create the de-bootcamp Project and Commit Hello Data World
Put everything from this chapter together: create a folder, initialize Git, write one line of Python, and push it to GitHub.
Open a terminal and run mkdir de-bootcamp && cd de-bootcamp.
Run git init to start tracking the folder.
Open the folder in VS Code with code .
Create a new file named hello.py containing one line: print("Hello, BrightCart!")
Run it from the VS Code terminal: python hello.py — confirm it prints the message.
On GitHub.com, create a new empty repository named de-bootcamp, then follow the "push an existing repository" instructions GitHub shows you to connect and git push.
Deliverable: A GitHub repository with one commit and a working hello.py file — this is repo #1 of your portfolio.
Chapter 1 Checklist — before moving on, you should be able to:
Run python --version and see a version number, not an error
Open a folder in VS Code using code .
Move between folders in a terminal using cd, ls, and mkdir
Explain the difference between git add, git commit, and git push
Have a GitHub repository containing your first commit
The building blocks every Python script is made of. We'll represent one BrightCart order in code and never let go of that example.
What Is a Variable?
A variable is a labeled box in your computer's memory. You put a value inside it, and you use the label to find that value again later. In Python you create one with a single equals sign — there's no need to declare a type up front, Python figures that out for you (this is called dynamic typing).
representing one row from customers.csv as variables
customer_id = "C001"
customer_name = "Ana Dela Cruz"
order_total = 792.34
is_paid = Trueprint(customer_name) # Output: Ana Dela Cruz
Read `=` as "gets set to", not "equals"
In math, x = 5 is a statement of fact. In Python, customer_id = "C001" is an instruction: take the value on the right, and store it in the box named on the left. That's why count = count + 1 makes perfect sense in code but none in algebra.
Variable Naming Rules
Python is strict about what a variable name can look like, and the community is strict about what it *should* look like. Both matter — one crashes your program, the other makes your code readable to the next engineer (often future-you).
Rule
Example
✓ snake_case (recommended)
customer_id, total_sales
✗ No spaces
customer id — this crashes
✗ No leading numbers
1st_order — this crashes
⚠ Case sensitive
Total and total are different variables
Core Data Types
Every value in Python has a type, and the type decides what you're allowed to do with it — you can add two numbers, but adding a number to text will raise an error. These four types cover the vast majority of what you'll touch in this bootcamp.
Type
Description
BrightCart example
str (string)
Text, always in quotes
"Manila"
int (integer)
Whole numbers
1001 (an order_id)
float
Decimal numbers
24.99 (a unit_price)
bool (boolean)
True or False
True (is_paid)
use type() any time you're unsure what you're working with
This trips up nearly every beginner exactly once: Python's input() function always returns a string, even if the person types a number. If you try to do math on it directly, Python either crashes or does something you didn't expect.
the beginner trap
qty = input("Quantity: ") # user types: 3
total = qty * 10# qty is the STRING "3", not the number 3# Python repeats the text instead of doing math:print(total) # "333333333333333333333333333333"
the fix — wrap input() in int() or float()
qty = int(input("Quantity: ")) # convert to int immediately
total = qty * 10print(total) # 30
Function
Converts to
int(x)
whole number
float(x)
decimal number
str(x)
text
Operators: Doing Things With Values
Operators are the symbols that combine values — arithmetic for math, comparisons for true/false questions, and logical operators for combining multiple conditions.
= assigns a value. == asks "are these equal?" and returns True/False. Mixing them up (if status = "paid":) is a syntax error in Python — which is actually a mercy; some languages let this typo through silently and cause much worse bugs.
Working With Strings
Text data from the real world is almost never clean — extra spaces, inconsistent capitalization, stray characters. String methods are the tools you'll reach for constantly, both here and later when cleaning BrightCart's messier exports in Phase 2.
string methods you'll use constantly
city = " manila "print(city.strip()) # "manila" (removes leading/trailing spaces)print(city.strip().title())# "Manila" (capitalizes each word)print(city.upper()) # " MANILA "print(city.lower()) # " manila "
name = "Ana" + " " + "Dela Cruz"print(name) # "Ana Dela Cruz" (+ joins/concatenates strings)
email = "ana.delacruz@brightmail.com"print(email.split("@")) # ['ana.delacruz', 'brightmail.com']# f-strings: the modern way to build text with variables insideprint(f"{name} spent ${line_total}")
Method
What it does
.strip()
removes leading/trailing whitespace
.upper() / .lower()
changes case
.title()
capitalizes each word
.split(sep)
breaks a string into a list on a separator
.replace(old, new)
swaps one substring for another
Chapter 2 Checklist — before moving on, you should be able to:
Create variables of type str, int, float, and bool
Explain why input() needs int() or float() around it for math
Use + - * / and comparison operators correctly
Clean a messy string using .strip(), .title(), or .lower()
Build a sentence with an f-string like f"{name} spent {total}"
How to make decisions, repeat work, and hold more than one value at a time — the tools that turn single-line scripts into real programs.
Conditionals: if / elif / else
Conditionals let your code make decisions. Python checks each condition top to bottom and runs the first block that evaluates to True — everything else is skipped.
classifying one BrightCart order
status = "paid"
amount = 792.34if status == "cancelled":
print("No action needed")
elif status == "paid"and amount >= 500:
print("VIP order — flag for priority shipping")
elif status == "paid":
print("Standard paid order")
else:
print("Needs follow-up: " + status)
Indentation is not optional
Python uses indentation (4 spaces, consistently) to know what's inside an if block and what isn't. Mixing tabs and spaces is one of the most common beginner errors — configure VS Code to insert spaces when you press Tab (it does this by default for Python files).
Loops: for and while
A for loop repeats code once per item in a collection — the workhorse of data engineering, since almost everything you process is a list of records. A while loop repeats until a condition becomes False, useful when you don't know in advance how many times you'll need to run.
for loop — summing a list of order amounts
order_amounts = [792.34, 59.99, 218.00, 38.97]
total = 0for amount in order_amounts:
total = total + amount
print(f"Total revenue: {total}") # Total revenue: 1109.3
while loop — a pattern you'll reuse for API pagination in Week 2
page = 1
has_more_pages = Truewhile has_more_pages:
print(f"Fetching page {page}...")
if page == 3: # pretend the API told us this was the last page
has_more_pages = False
page += 1# same as: page = page + 1
The infinite loop
A while loop with a condition that never becomes False will run forever and freeze your program. Always make sure something inside the loop moves you toward the exit condition — in the example above, that's page += 1.
Lists: An Ordered Collection
A list holds multiple values in one variable, in order, accessed by position (starting at 0, not 1 — this catches everyone once). Lists are the closest thing Python has to a spreadsheet column, and you'll use them constantly before you meet pandas in Phase 2.
lists — the last line is a list comprehension, a compact filter (optional, come back to this later if it's confusing)
cities = ["Manila", "Cebu City", "Davao City", "Singapore"]
print(cities[0]) # "Manila" <- index 0 is the FIRST itemprint(cities[-1]) # "Singapore" <- -1 is the LAST itemprint(len(cities)) # 4
cities.append("Tokyo") # add to the endprint(cities) # ['Manila', 'Cebu City', 'Davao City', 'Singapore', 'Tokyo']
philippine_cities = [c for c in cities if c != "Singapore"and c != "Tokyo"]
print(philippine_cities) # ['Manila', 'Cebu City', 'Davao City']
Dictionaries: Labeled Data
A dictionary stores key–value pairs instead of positions — this is how you'll represent one row of a table before pandas does it for you automatically. If a list is a column, a dictionary is closer to a single record.
dictionaries — one BrightCart order as a record
order = {
"order_id": 1001,
"customer": "Ana Dela Cruz",
"amount": 792.34,
"status": "paid"
}
print(order["customer"]) # "Ana Dela Cruz"print(order.get("status")) # "paid"print(order.get("discount", 0)) # 0 <- .get() with a default avoids a crash if the key is missing
order["shipped"] = True# add a new keyprint(order.keys()) # dict_keys(['order_id', 'customer', 'amount', 'status', 'shipped'])
A list of dictionaries is exactly how you'll represent a whole table — and it's exactly the shape a JSON API response usually arrives in, which is why this pattern matters so much.
Functions turn repeated code into reusable tools. Files and APIs are how real data gets into your programs in the first place.
Functions: Reusable Blocks of Logic
A function is a named, reusable block of code. Instead of retyping the same logic for every order, you write it once, give it a name, and call it as many times as you need with different inputs (called parameters or arguments).
anatomy of a function: def, parameters, a default value, and return
defcalculate_line_total(quantity, unit_price):
return quantity * unit_price
# call it as many times as you need:print(calculate_line_total(2, 24.99)) # 49.98print(calculate_line_total(1, 129.00)) # 129.0defclassify_order(amount, threshold=500):
"""threshold has a DEFAULT value -- callers can override it or skip it"""if amount >= threshold:
return"VIP"return"standard"print(classify_order(792.34)) # "VIP" (uses default threshold=500)print(classify_order(300, 250)) # "VIP" (override: threshold=250)
Part
What it means
def
keyword that starts a function definition
calculate_line_total
the function's name — use a verb, describe what it does
(quantity, unit_price)
parameters — the inputs the function expects
return
sends a value back to whoever called the function
A good function does one thing
If you find yourself describing a function with "and" ("it calculates the total and prints a receipt and saves to a file"), split it into three functions. Small, single-purpose functions are dramatically easier to test and reuse — this habit pays off enormously once you're writing ETL pipelines in Phase 2.
Handling Errors: try / except
Real data is unpredictable — a field might be blank, a file might not exist, an API might time out. try / except lets your program catch an error and respond sensibly instead of crashing.
skip bad values instead of letting one bad row kill the whole script
raw_quantities = ["2", "3", "N/A", "1"]
total_units = 0for q in raw_quantities:
try:
total_units += int(q)
except ValueError:
print(f"Skipping bad value: {q!r}")
print(total_units) # 6 (the 'N/A' was skipped, not crashed on)
Don't catch every error blindly
A bare except: (with no error type) silently swallows everything, including bugs in your own code — making them nearly impossible to find. Always name the error you expect, like except ValueError:.
WEEK 1PORTFOLIO PROJECT
Portfolio Project — BrightCart Order Total Calculator
None
Write a function calculate_line_total(quantity, unit_price) that returns the line total.
Write a function classify_order(amount) that returns "VIP" for totals ≥ 500 and "standard" otherwise.
Build a small command-line loop using input() that lets a user enter a quantity and price, prints the total, and asks "Add another item? (y/n)" until they say no.
Print a final receipt-style summary and commit the script as order_calculator.py.
Reading & Writing CSV Files
Before pandas (Phase 2) makes this effortless, it's worth understanding what's actually happening: a CSV file is plain text with commas separating values, and Python's built-in csv module turns each line into a list — or, with DictReader, into a dictionary using the header row as keys.
reading customers.csv with csv.DictReader — each row becomes a dictionary
import csv
withopen("data/customers.csv", "r") as f:
reader = csv.DictReader(f)
for row in reader:
print(row["first_name"], row["city"])
# Ana Manila# Ben Cebu City# Carlo Davao City# ...
writing a new CSV file — the pattern you'll reuse for every pipeline's output
The with keyword automatically closes the file when you're done with it, even if an error happens inside the block. Always use with open(...) instead of a bare open(...) — it's the difference between a script that occasionally corrupts files and one that never does.
Working With JSON & APIs
JSON (JavaScript Object Notation) is the format almost every API speaks — and it maps directly onto Python dictionaries and lists, which is exactly why you learned those first. An API is just a website's URL that returns data instead of a webpage.
a JSON order record — notice it's basically a Python dict with a list inside it
json.load() turns JSON text into Python dictionaries/lists automatically
import json
withopen("data/order_1001.json") as f:
order = json.load(f) # JSON text -> Python dictprint(order["customer"]) # "Ana Dela Cruz"for item in order["items"]:
print(item["product"], item["quantity"])
requests — the standard library for calling real APIs (install with: pip install requests)
import requests
response = requests.get("https://api.example.com/orders", timeout=30)
response.raise_for_status() # crash loudly if the request failed, instead of silently continuing
data = response.json() # parses JSON straight into a Python dict/listprint(data[0]["customer"])
Always set a timeout
Without timeout=30, a stalled server can freeze your script forever with no error at all. This one keyword argument will save you a genuinely confusing debugging session down the road.
WEEK 2PORTFOLIO PROJECT
Portfolio Project — BrightCart Order Extractor
None
Write a function load_orders_csv(path) that reads orders.csv with csv.DictReader and returns a list of dictionaries.
Write a function summarize_by_status(orders) that counts how many orders fall into each status.
Read one sample order as JSON (create a small .json file by hand) and print its item list.
Write the status summary out to a new file, status_summary.csv, using csv.DictWriter.
Commit the script as order_extractor.py — this is the direct ancestor of the ETL pipelines you'll build in Phase 2.
Chapter 4 Checklist — before moving on, you should be able to:
Write a function with parameters, a default value, and a return statement
Explain why try / except is safer than letting a script crash on bad data
Read a CSV file into a list of dictionaries with csv.DictReader
Write a list of dictionaries out to a new CSV file with csv.DictWriter
Explain what JSON is and why json.load() turns it directly into Python dicts/lists
SQL is how you ask a database questions. We'll load BrightCart into a real database and start asking it things.
What Is a Relational Database?
A relational database stores data in tables — grids of rows and columns, like a spreadsheet, except tables can be linked together by shared ID columns (that's the "relational" part). PostgreSQL, MySQL, and SQLite are all RDBMS (Relational Database Management Systems) — programs that store and let you query this kind of data.
Python variables live only while your script runs. A database persists — BrightCart's orders stay there whether your laptop is on or off, and multiple programs (or people) can read and write to it at the same time. That durability and shareability is exactly why production systems use databases instead of just CSV files.
Tool Spotlight: PostgreSQL
20 min
Tool Spotlight
PostgreSQL ("Postgres")
A free, open-source relational database — the one this entire bootcamp is built around.
What it is
PostgreSQL is a server program: it runs quietly in the background on your computer (or in the cloud, later in Phase 2) and listens for SQL commands from clients like the terminal, VS Code, or a Python script.
Why data engineers use it
It's free, it's what most real companies actually use for OLTP workloads, and everything you learn here — joins, window functions, indexes — transfers directly to Snowflake, BigQuery, and Redshift in Phase 2.
How to get it running
1
Go to postgresql.org/download and choose your OS (the Windows/Mac installer bundles pgAdmin, a GUI client, automatically).
2
During install you'll set a password for the default postgres user — write it down, you'll need it constantly.
3
Keep the default port, 5432, unless something else is already using it.
4
Once installed, open a terminal and connect with psql -U postgres to confirm it's running.
Try it
terminal — psql is Postgres's own command-line client
$ psql -U postgres
Password for user postgres: ********
psql (16.2)
Type "help" for help.
postgres=# CREATE DATABASE brightcart;
CREATE DATABASE
postgres=# \q
Common snag: "psql: command not found" usually means Postgres's bin folder isn't on your PATH. On Windows, search for "SQL Shell (psql)" in the Start Menu instead — it's installed alongside the database and skips the PATH issue entirely.
Tool Spotlight: A SQL Client (DBeaver / pgAdmin)
10 min
Tool Spotlight
DBeaver (or pgAdmin)
A visual window into your database — browse tables, run queries, and see results in a grid.
What it is
A SQL client is a GUI application that connects to a database server and lets you explore tables, write and run queries, and view results — without typing raw psql commands for everything.
Why data engineers use it
You'll write plenty of SQL directly, but a visual table browser makes it dramatically faster to sanity-check your data ("does this table actually have 45 rows like I expect?") before you write a single query.
How to get it running
1
Download DBeaver Community Edition (free) from dbeaver.io — it works with every database you'll meet in this bootcamp, not just Postgres.
2
Open DBeaver, click New Database Connection, choose PostgreSQL.
3
Enter host localhost, port 5432, database brightcart, username postgres, and the password you set during install.
4
Click Test Connection, then Finish.
Common snag: pgAdmin (bundled with the Postgres installer) does the same job and is a perfectly good alternative — use whichever one you already have open.
Loading BrightCart Into Postgres
Time to turn the .sql files from the Introduction into a real, queryable database. You'll run the schema file first (it creates empty tables) and then the seed file (it fills them with data).
terminal — run these from the folder containing the .sql files
In DBeaver or pgAdmin, you can instead open each .sql file and click "Execute Script" (not just "Execute" — that only runs one statement). Same result, different interface.
SELECT, WHERE, ORDER BY, LIMIT
Every SQL query starts the same way: tell it what columns you want, from which table, which rows qualify, and how to sort the result. This one pattern answers most of the questions a business ever asks.
the 5 most fundamental keywords in SQL, in the order you'll type them
SELECT first_name, last_name, city
FROM customers
WHERE country = 'Philippines'ORDER BY city ASCLIMIT5;
Clause
What it does
SELECT
which columns to return (or * for all of them)
FROM
which table to read
WHERE
filters rows before they're returned
ORDER BY
sorts the result — add DESC for descending
LIMIT
caps how many rows come back
a realistic BrightCart question: "what's still waiting on us?"
-- Every order that is still pending, most recent firstSELECT order_id, order_date, payment_method
FROM orders
WHERE status = 'pending'ORDER BY order_date DESC;
SQL strings use single quotes
WHERE status = "paid" will error in Postgres — use single quotes for text: WHERE status = 'paid'. Double quotes in SQL mean something entirely different (they quote column/table names, not values).
Aggregate Functions & GROUP BY
Aggregate functions collapse many rows into one summary number — count, sum, average, min, max. GROUP BY is what lets you get one summary per category instead of one summary for the whole table.
Function
Returns
COUNT(*)
number of rows
SUM(col)
total
AVG(col)
average
MIN(col) / MAX(col)
smallest / largest value
how many orders are in each status?
SELECT status, COUNT(*) AS order_count
FROM orders
GROUP BY status
ORDER BY order_count DESC;
status
order_count
paid
39
pending
3
cancelled
2
refunded
1
HAVING filters groups AFTER aggregation — WHERE can't do this, it runs before grouping
-- Total revenue per payment method, but only for orders worth summarizingSELECT payment_method,
SUM(quantity * unit_price) AS revenue,
COUNT(*) AS line_items
FROM order_items oi
JOIN orders o ON o.order_id = oi.order_id
WHERE o.status = 'paid'GROUP BY payment_method
HAVINGSUM(quantity * unit_price) > 200ORDER BY revenue DESC;
WHERE vs HAVING
WHERE filters individual rows before grouping happens. HAVING filters the groups after SUM/COUNT/etc. have already been calculated. If your condition mentions an aggregate function, it has to go in HAVING.
Chapter 5 Checklist — before moving on, you should be able to:
Explain what a relational database is and why it's more durable than a CSV file
Load the BrightCart schema and seed data into a real Postgres database
Write a SELECT ... WHERE ... ORDER BY ... LIMIT query from memory
Real BrightCart questions almost always need more than one table. This is how you combine them.
Primary Keys, Foreign Keys & the ERD
A primary key uniquely identifies each row in a table (customer_id in customers). A foreign key is a column in one table that points at a primary key in another (orders.customer_id points at customers.customer_id). This is the entire mechanism that lets SQL connect tables together.
JOIN Types
A JOIN combines rows from two tables based on a matching column. Which rows survive when there isn't a match depends entirely on which join type you pick — this is the single most important SQL concept to get comfortable with.
Join type
Keeps...
INNER JOIN
only rows that match in both tables
LEFT JOIN
all rows from the left table, matched data if it exists, else NULL
RIGHT JOIN
all rows from the right table (rarely needed — just swap table order and use LEFT)
FULL JOIN
everything from both sides, matched where possible
The "find what's missing" pattern
LEFT JOIN ... WHERE right_table.id IS NULL is how you find customers with no orders, products that never sold, or — later in Phase 2 — records that failed to load. Memorize this shape.
Window Functions
A window function calculates something across a set of related rows without collapsing them into one row the way GROUP BY does — you keep every row, and get a ranking, running total, or comparison alongside it. This is the tool that makes "top N per group" and "running total" questions easy.
Function
Returns
ROW_NUMBER()
1, 2, 3... with no ties
RANK()
ties share a rank, next rank skips (1, 1, 3)
SUM() / AVG() OVER (...)
running total / running average
LAG() / LEAD()
the previous / next row's value
Chapter 6 Checklist — before moving on, you should be able to:
Explain the difference between INNER JOIN and LEFT JOIN in one sentence
Use LEFT JOIN ... WHERE x IS NULL to find "missing" records
Rewrite a nested subquery as a WITH CTE
Use ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...) to rank rows within a group
The tables you query for daily operations aren't the tables you want to build dashboards on. Here's the design pattern that fixes that.
OLTP vs OLAP
customers, orders, and order_items are OLTP tables (Online Transaction Processing) — designed so BrightCart's checkout page can insert one order quickly and safely. A dashboard asking "what were our total sales by category, by month?" has completely different needs — that's OLAP (Online Analytical Processing), and the design pattern for it is called a star schema.
The Star Schema: Facts & Dimensions
A fact table holds the numbers you want to measure (sales_amount, quantity) plus foreign keys pointing out to dimension tables, which hold the descriptive context (who, what, when).
Chapter 7 Checklist — before moving on, you should be able to:
Explain OLTP vs OLAP in one sentence each
Draw (or describe) a star schema with one fact table and at least two dimensions
Build fact_sales and at least two dimension views from the BrightCart OLTP tables
You went from an empty folder to a working reporting layer on a real database. Here's what that's actually worth.
What You Can Now Do
Python
Variables, control flow, functions, file I/O, and basic JSON/API handling — the full toolkit for scripting a data task.
Git & GitHub
Track changes with commits and publish a public portfolio employers can actually look at.
SQL
SELECT through window functions — enough to answer almost any business question a stakeholder asks.
Dimensional Modeling
Design a star schema and know when a view, materialized view, or table is the right tool.
Coming Up in Phase 2: Weeks 5–8
Phase 1 was about talking to data one script and one query at a time. Phase 2 is about making that repeatable — turning BrightCart's dirty_orders.csv into an automated pipeline that cleans itself, and moving your work from your laptop into the cloud.
Ready to keep going?
Phase 2 (pandas, ETL pipelines, cloud, warehousing) and Phase 3 (Airflow, Spark, Docker, capstone) continue below — the first chapter of each is free too.