SQL Quest

Lessons, hints, explained solutions, the in-browser PostgreSQL editor and practice answer checks in your browser are available. Graded assessments, recorded progress from answer checks and new certificates aren't available yet.

Sooq Analytics event store

Sooq Analytics (fictional) · 4 tables · 420,200 rows · version 1

Open in the playground

Large tables for query-performance lessons: 20,000 users, 300,000 tracking events and 100,000 orders during 2025, plus a small product list. Only primary keys are indexed, so filters and joins on other columns start as sequential scans; you create the indexes and compare plans. Columns are chosen to show selectivity: email is almost unique, status is dominated by one value, and event_type and user activity are heavily skewed.

The documentation is open to everyone. Querying this dataset in the playground is included with Formula Quest membership; lessons that use it follow their own access.

Relationships

products200PKproduct_idproduct_namecategorypriceusers20,000PKuser_idemailfull_namecountryplansignup_atlast_login_atevents300,000PKevent_idFKuser_idevent_typeoccurred_atcountrydevicepropertiesorders100,000PKorder_idFKuser_idFKproduct_idquantityamountstatuscreated_atcoupon_code
Arrows point from a foreign key to the table it references. PK = primary key, FK = foreign key. Numbers are row counts.

Sample queries

Plan for a lookup by email
EXPLAIN
SELECT *
FROM users
WHERE email = 'user12345@example.net';
Events for one user (with timing)
EXPLAIN ANALYZE
SELECT event_type, count(*)
FROM events
WHERE user_id = 42
GROUP BY event_type;
Pending orders
EXPLAIN
SELECT order_id, user_id, amount
FROM orders
WHERE status = 'pending';
Event mix
SELECT event_type, count(*) AS events,
       round(100.0 * count(*) / sum(count(*)) OVER (), 1) AS pct
FROM events
GROUP BY event_type
ORDER BY events DESC;
Daily purchases in March
SELECT occurred_at::date AS day, count(*) AS purchases
FROM events
WHERE event_type = 'purchase'
  AND occurred_at >= '2025-03-01' AND occurred_at < '2025-04-01'
GROUP BY day
ORDER BY day;

Data dictionary

products

200 rows · primary key (product_id)

Products sold on the marketplace (small lookup table).

ColumnTypeNull?KeyDescription
product_idintegerno PKUnique product number.
product_nametextnoProduct name.
categorytextnoProduct category (10 categories).
pricenumeric(12,2)noUnit price in SAR.

users

20,000 rows · primary key (user_id)

Registered marketplace users (20,000). Only user_id is indexed.

ColumnTypeNull?KeyDescription
user_idintegerno PKUnique user number. Users were numbered in signup order.
emailtextnoEmail address. Unique in practice, but there is no unique constraint or index (high selectivity).
full_nametextnoUser's name (many users share a name).
countrychar(2)noISO country code; about 60% are SA.
plantextnoSubscription plan: free (about 80%), plus or pro.
signup_attimestampnoWhen the user registered (2021–2024).
last_login_attimestampyesMost recent login during 2025. NULL if the user never logged in.

events

300,000 rows · primary key (event_id)

Tracking events (300,000) recorded during 2025, inserted in time order. Only event_id is indexed.

ColumnTypeNull?KeyDescription
event_idbigintno PKUnique event number, increasing with time.
user_idintegerno users.user_idUser who triggered the event. Not indexed; a few power users have thousands of events.
event_typetextnopage_view (about 70%), click, search, add_to_cart, checkout_start, purchase, login or refund (rare). Skewed.
occurred_attimestampnoWhen the event happened (2025, UTC). Increases with event_id.
countrychar(2)noCountry the event came from (usually the user's country).
devicetextnomobile, desktop or tablet.
propertiestextnoExtra details as JSON text, e.g. {"page": "/p/12", "session": 81}. Cast to jsonb to query it.

orders

100,000 rows · primary key (order_id)

Marketplace orders (100,000) placed during 2025. Only order_id is indexed.

ColumnTypeNull?KeyDescription
order_idintegerno PKUnique order number, increasing with time.
user_idintegerno users.user_idUser who placed the order. Not indexed.
product_idintegerno products.product_idProduct ordered. Not indexed.
quantitysmallintnoUnits ordered (1–3).
amountnumeric(12,2)noOrder value in SAR (price × quantity).
statustextnocompleted (about 95%), pending, cancelled or refunded. Low selectivity.
created_attimestampnoWhen the order was placed (2025, UTC).
coupon_codetextyesCoupon used. NULL for most orders.