Sandbox Reference

Sandbox Schema & Relationships

A map of every table and column in the sandbox database β€” and how they connect. Read this before writing JOINs so you know exactly which column links to which table.

What am I looking at?

The sandbox is a PostgreSQL database with five pre-loaded datasets β€” e-commerce, finance, a chatbot log, maps, and health. Every lesson, the Playground, and the DBA tool all run queries against these exact tables. This page is your map: it shows every table, every column, and the relationships (foreign keys) that let you JOIN them together.

When you write a JOIN, you need to tell the database which column on table A matches which column on table B. Foreign keys are those columns. For example, orders.customer_id points to customers.id β€” so you join on that pair.

Legend

  • πŸ”‘ PKPrimary key β€” the unique identifier for each row in the table. Always id (or symbol in securities).
  • πŸ”— FKForeign key β€” this column β€œjoins to” another table’s primary key. The badge shows the target (e.g. customers.id).
  • πŸ”— softText-match / soft link β€” a logical relationship (e.g. products.category matches categories.name) that is not an enforced database constraint. You can still join on it, but no referential integrity is guaranteed.

E-commerce

public

Products, customers, orders, line items, categories.

products

  • idπŸ”‘ PK
  • name
  • price
  • categoryπŸ”— β†’ categories.name
  • in_stock
  • created_at

customers

  • idπŸ”‘ PK
  • name
  • email
  • country
  • signed_up

orders

  • idπŸ”‘ PK
  • customer_idπŸ”— β†’ customers.id
  • placed_at
  • status

order_items

  • idπŸ”‘ PK
  • order_idπŸ”— β†’ orders.id
  • product_idπŸ”— β†’ products.id
  • quantity
  • unit_price

categories

  • idπŸ”‘ PK
  • name

How these tables join

  • orders.customer_idβ†’customers.id
  • order_items.order_idβ†’orders.id
  • order_items.product_idβ†’products.id
  • products.categoryβ†’categories.nametext match, not an enforced FK

Example JOIN

SELECT
  c.name        AS customer,
  o.placed_at,
  p.name        AS product,
  oi.quantity,
  oi.unit_price
FROM orders o
JOIN customers c       ON c.id        = o.customer_id
JOIN order_items oi    ON oi.order_id  = o.id
JOIN products p        ON p.id        = oi.product_id
WHERE o.status = 'shipped'
ORDER BY o.placed_at DESC
LIMIT 20;

Finance

finance

Customers, accounts, ~200k transactions, securities, holdings, trades.

finance.customers

  • idπŸ”‘ PK
  • name
  • email
  • country
  • kyc_status
  • risk_score
  • opened_at

finance.accounts

  • idπŸ”‘ PK
  • customer_idπŸ”— β†’ finance.customers.id
  • account_type
  • currency
  • balance
  • is_active
  • opened_at

finance.transactions

  • idπŸ”‘ PK
  • account_idπŸ”— β†’ finance.accounts.id
  • amount
  • kind
  • category
  • merchant_name
  • occurred_at
  • status

finance.securities

  • symbolπŸ”‘ PK
  • name
  • sector
  • exchange
  • ipo_date

finance.holdings

  • account_idπŸ”— β†’ finance.accounts.id
  • symbolπŸ”— β†’ finance.securities.symbol
  • quantity
  • avg_cost

finance.trades

  • idπŸ”‘ PK
  • account_idπŸ”— β†’ finance.accounts.id
  • symbolπŸ”— β†’ finance.securities.symbol
  • side
  • quantity
  • price
  • executed_at

How these tables join

  • accounts.customer_idβ†’customers.id
  • transactions.account_idβ†’accounts.id
  • holdings.account_idβ†’accounts.id
  • holdings.symbolβ†’securities.symbol
  • trades.account_idβ†’accounts.id
  • trades.symbolβ†’securities.symbol

Example JOIN

SELECT
  c.name          AS customer,
  a.account_type,
  a.currency,
  SUM(t.amount)   AS total_spent
FROM finance.accounts a
JOIN finance.customers c     ON c.id         = a.customer_id
JOIN finance.transactions t  ON t.account_id = a.id
WHERE t.kind = 'debit'
  AND t.occurred_at >= now() - interval '90 days'
GROUP BY c.name, a.account_type, a.currency
ORDER BY total_spent DESC
LIMIT 10;

Chatbot

chatbot

Users, conversations, ~200k messages, intents, feedback ratings.

chatbot.users

  • idπŸ”‘ PK
  • email
  • display_name
  • plan
  • signed_up

chatbot.intents

  • idπŸ”‘ PK
  • name
  • category
  • description

chatbot.conversations

  • idπŸ”‘ PK
  • user_idπŸ”— β†’ chatbot.users.id
  • intent_idπŸ”— β†’ chatbot.intents.id
  • title
  • model
  • started_at
  • ended_at

chatbot.messages

  • idπŸ”‘ PK
  • conversation_idπŸ”— β†’ chatbot.conversations.id
  • role
  • tokens_in
  • tokens_out
  • content_preview
  • created_at

chatbot.feedback

  • idπŸ”‘ PK
  • message_idπŸ”— β†’ chatbot.messages.id
  • user_idπŸ”— β†’ chatbot.users.id
  • rating
  • thumbs_up
  • comment
  • created_at

How these tables join

  • conversations.user_idβ†’users.id
  • conversations.intent_idβ†’intents.id
  • messages.conversation_idβ†’conversations.id
  • feedback.message_idβ†’messages.id
  • feedback.user_idβ†’users.id

Example JOIN

SELECT
  u.display_name,
  i.name           AS intent,
  COUNT(m.id)      AS message_count,
  AVG(f.rating)    AS avg_rating
FROM chatbot.conversations cv
JOIN chatbot.users u     ON u.id              = cv.user_id
JOIN chatbot.intents i   ON i.id              = cv.intent_id
JOIN chatbot.messages m  ON m.conversation_id = cv.id
LEFT JOIN chatbot.feedback f ON f.message_id  = m.id
GROUP BY u.display_name, i.name
ORDER BY message_count DESC
LIMIT 10;

Maps

maps

Cities, 50k places (POIs), 200k reviews, users.

maps.cities

  • idπŸ”‘ PK
  • name
  • country
  • lat
  • lng
  • population

maps.categories

  • idπŸ”‘ PK
  • name
  • icon

maps.places

  • idπŸ”‘ PK
  • name
  • categoryπŸ”— β†’ maps.categories.name
  • city_idπŸ”— β†’ maps.cities.id
  • lat
  • lng
  • rating
  • review_count
  • price_level
  • is_open_now

maps.users

  • idπŸ”‘ PK
  • name
  • country
  • signed_up
  • reviews

maps.reviews

  • idπŸ”‘ PK
  • place_idπŸ”— β†’ maps.places.id
  • user_idπŸ”— β†’ maps.users.id
  • rating
  • comment
  • visited_at
  • created_at

How these tables join

  • places.city_idβ†’cities.id
  • reviews.place_idβ†’places.id
  • reviews.user_idβ†’users.id
  • places.categoryβ†’categories.nametext match, not an enforced FK

Example JOIN

SELECT
  pl.name           AS place,
  ci.name           AS city,
  ROUND(AVG(r.rating)::numeric, 2) AS avg_rating,
  COUNT(r.id)       AS review_count
FROM maps.places pl
JOIN maps.cities ci    ON ci.id       = pl.city_id
JOIN maps.reviews r    ON r.place_id  = pl.id
GROUP BY pl.name, ci.name
HAVING COUNT(r.id) >= 5
ORDER BY avg_rating DESC
LIMIT 10;

Health

health

Hospitals, providers, patients, 100k visits, 60k prescriptions, 80k lab results. Synthetic only.

health.hospitals

  • idπŸ”‘ PK
  • name
  • city
  • country
  • beds
  • type

health.providers

  • idπŸ”‘ PK
  • name
  • specialty
  • npi
  • hospital_idπŸ”— β†’ health.hospitals.id
  • hired_at

health.patients

  • idπŸ”‘ PK
  • name
  • dob
  • sex
  • blood_type
  • country
  • registered_at

health.visits

  • idπŸ”‘ PK
  • patient_idπŸ”— β†’ health.patients.id
  • provider_idπŸ”— β†’ health.providers.id
  • visit_date
  • visit_type
  • primary_complaint
  • diagnosis_code
  • follow_up

health.prescriptions

  • idπŸ”‘ PK
  • visit_idπŸ”— β†’ health.visits.id
  • drug_name
  • dosage
  • frequency
  • days_supply
  • prescribed_at

health.lab_results

  • idπŸ”‘ PK
  • patient_idπŸ”— β†’ health.patients.id
  • test_name
  • result_value
  • unit
  • normal_low
  • normal_high
  • is_abnormal
  • taken_at

How these tables join

  • providers.hospital_idβ†’hospitals.id
  • visits.patient_idβ†’patients.id
  • visits.provider_idβ†’providers.id
  • prescriptions.visit_idβ†’visits.id
  • lab_results.patient_idβ†’patients.id

Example JOIN

SELECT
  p.name           AS patient,
  v.visit_date,
  pr.name          AS provider,
  h.name           AS hospital,
  v.primary_complaint,
  v.diagnosis_code
FROM health.visits v
JOIN health.patients p    ON p.id          = v.patient_id
JOIN health.providers pr  ON pr.id         = v.provider_id
JOIN health.hospitals h   ON h.id          = pr.hospital_id
WHERE v.visit_date >= now() - interval '30 days'
ORDER BY v.visit_date DESC
LIMIT 10;

Ready to write your own JOINs?

Fire up the Playground and run a query against these tables, or start a lesson and practice with guided exercises.

Sandbox Schema & ER Diagram Β· Schema School