SQL module
Getting started
Your SQL practice environment
Start Postgres with Compose, install psql, and seed the retail CSVs used in every SQL lesson.
beginner45 minRun on your machine
What you will be able to do
- Run Postgres with Compose on port 15432
- Connect with psql and confirm three seeded tables
- Create the ~/dcubes/sql-lab folder used in later lessons
Why this exists
SQL is how you ask the warehouse questions. A website that ran queries for you would not teach the skill. You type. Your machine runs Postgres.
Do Docker Getting Started first. Python is useful context (First Python data shift) but not required — this page loads the same retail shape from static CSVs.
Install the Postgres client
Linux / WSL (Ubuntu)
sudo apt update
sudo apt install --yes postgresql-client
psql --version
macOS
brew install libpq
brew link --force libpq
psql --version
Or install Postgres.app / full Postgres — you only need the psql client for this module.
Lab folder and samples
mkdir -p ~/dcubes/sql-lab/data ~/dcubes/sql-lab/out
cd ~/dcubes/sql-lab
Download the seed files (right-click save if needed, then copy into data/):
# After saving to Downloads — adjust the path for your OS / WSL
cp ~/Downloads/stores.csv ~/Downloads/products.csv ~/Downloads/orders.csv ~/dcubes/sql-lab/data/
ls data
wc -l data/*.csv
You should see three files; orders.csv has a header plus 300 data rows (wc -l → 301).
Start Postgres
cd ~/dcubes/sql-lab
cat > compose.yaml << 'EOF'
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: dcubes
POSTGRES_PASSWORD: dcubes
POSTGRES_DB: retail
ports:
- "15432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dcubes -d retail"]
interval: 3s
timeout: 3s
retries: 20
volumes:
pgdata:
EOF
docker compose up -d
docker compose ps
Wait until healthy. Host port is 15432.
Create tables and load
cd ~/dcubes/sql-lab
export PGPASSWORD=dcubes
psql -h 127.0.0.1 -p 15432 -U dcubes -d retail << 'SQL'
CREATE TABLE IF NOT EXISTS stores (
store_id TEXT PRIMARY KEY,
store_name TEXT NOT NULL,
region TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS products (
sku TEXT PRIMARY KEY,
product_name TEXT NOT NULL,
category TEXT NOT NULL,
unit_price NUMERIC(10, 2) NOT NULL
);
CREATE TABLE IF NOT EXISTS orders (
order_id TEXT PRIMARY KEY,
order_ts TIMESTAMPTZ NOT NULL,
store_id TEXT NOT NULL REFERENCES stores (store_id),
customer_id TEXT NOT NULL,
sku TEXT NOT NULL REFERENCES products (sku),
product_name TEXT NOT NULL,
category TEXT NOT NULL,
quantity INTEGER NOT NULL,
unit_price NUMERIC(10, 2) NOT NULL,
line_total NUMERIC(12, 2) NOT NULL,
payment_method TEXT NOT NULL,
status TEXT NOT NULL
);
TRUNCATE orders, products, stores CASCADE;
SQL
Load CSVs with \copy (runs on the client, reads host paths):
cd ~/dcubes/sql-lab
export PGPASSWORD=dcubes
psql -h 127.0.0.1 -p 15432 -U dcubes -d retail -c "\copy stores FROM 'data/stores.csv' CSV HEADER"
psql -h 127.0.0.1 -p 15432 -U dcubes -d retail -c "\copy products FROM 'data/products.csv' CSV HEADER"
psql -h 127.0.0.1 -p 15432 -U dcubes -d retail -c "\copy orders FROM 'data/orders.csv' CSV HEADER"
psql -h 127.0.0.1 -p 15432 -U dcubes -d retail -c "SELECT 'stores' AS t, count(*) FROM stores UNION ALL SELECT 'products', count(*) FROM products UNION ALL SELECT 'orders', count(*) FROM orders;"
t | count
----------+-------
stores | 5
products | 12
orders | 300
Connection shortcut
export PGPASSWORD=dcubes
export PGHOST=127.0.0.1
export PGPORT=15432
export PGUSER=dcubes
export PGDATABASE=retail
psql -c "SELECT count(*) FROM orders;"
Common mistakes
- Port 5432 — this lab uses 15432.
- COPY vs \copy — server
COPYcannot see your laptop path; use\copyfrompsql. - FK order — load
storesandproductsbeforeorders. - Docker not running — connection refused.