You appear to be using an ad blocker. We rely on advertising to fund this free service.
Please disable your ad blocker for our site to continue.
How to disable your ad blocker:
Click the AdBlock icon in your browser extensions.
Select "Don't run on pages on this site".
Click "Exclude".
Screenshot: AdBlock disable instructions
Click the AdBlock Plus icon in your browser extensions.
Click the blue toggle switch next to "This website" so it turns gray.
Screenshot: AdBlock Plus disable instructions
Click the uBlock Origin icon in your browser extensions.
Click the large blue power button. It will turn gray.
Screenshot: uBlock Origin disable instructions
Find your ad blocker icon (usually in the top right corner).
Follow its specific instructions to disable it for this site (often called "whitelisting" or "pausing").
Check DNS / Router Settings
Ad blocking might be happening at your network level (DNS settings on your phone/router or a network-wide blocker like Pi-hole).
Check your device's Wi-Fi / network settings for custom DNS servers (like Cloudflare, Google DNS, or AdGuard DNS). Try changing to your ISP's default DNS or another standard provider.
Check your router's configuration page (often 192.168.1.1 or similar) for DNS or parental control settings that might be blocking ads.
If using a network-wide blocker (like Pi-hole), consult its documentation to whitelist this site.
Stop hunting for JOIN syntax at 2am. Practice the queries you write all the time until the keywords come automatically.
🏆NEW BEST!
SELECT with JOIN
A multi-table join with aliases and filtering. Keeping track of aliases is where most people slow down.
Part 1 of 1
SELECT u.id, u.name, u.email, COUNT(o.id) AS order_count, SUM(o.total) AS lifetime_value FROM users u LEFT JOIN orders o ON o.user_id = u.id WHERE u.created_at >= '2024-01-01' GROUP BY u.id, u.name, u.email ORDER BY lifetime_value DESC;
WPM0
Accuracy100%
Progress0%
Streak0🔥
SpeedTarget: 30 WPM
▼
⏱️Start typing...
⚠️Continue typing or resetting in 3...
🏆NEW BEST!
Subquery
A correlated subquery inside a WHERE clause. The nested SELECT is the part that requires careful typing.
Part 1 of 1
SELECT p.id, p.name, p.price FROM products p WHERE p.price > (SELECT AVG(price) FROM products WHERE category_id = p.category_id) AND p.stock_count > 0 ORDER BY p.price ASC;
WPM0
Accuracy100%
Progress0%
Streak0🔥
SpeedTarget: 30 WPM
▼
⏱️Start typing...
⚠️Continue typing or resetting in 3...
🏆NEW BEST!
CREATE TABLE
A table definition with column types, constraints, and a foreign key. DDL syntax trips up even experienced people.
Part 1 of 1
CREATE TABLE order_items (id SERIAL PRIMARY KEY, order_id INTEGER NOT NULL REFERENCES orders(id) ON DELETE CASCADE, product_id INTEGER NOT NULL REFERENCES products(id), quantity INTEGER NOT NULL DEFAULT 1, unit_price NUMERIC(10, 2) NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW());
WPM0
Accuracy100%
Progress0%
Streak0🔥
SpeedTarget: 25 WPM
▼
⏱️Start typing...
⚠️Continue typing or resetting in 3...
🏆NEW BEST!
INSERT with Values
A multi-row INSERT with ON CONFLICT handling. The RETURNING clause is easy to forget.
Part 1 of 1
INSERT INTO users (name, email, role, created_at) VALUES ('Alice Chen', '[email protected]', 'admin', NOW()), ('Bob Torres', '[email protected]', 'user', NOW()) ON CONFLICT (email) DO NOTHING RETURNING id, email;
WPM0
Accuracy100%
Progress0%
Streak0🔥
SpeedTarget: 28 WPM
▼
⏱️Start typing...
⚠️Continue typing or resetting in 3...
🏆NEW BEST!
UPDATE with WHERE
An UPDATE that joins to another table before applying changes. Using a FROM clause makes this PostgreSQL-specific.
Part 1 of 1
UPDATE orders SET status = 'shipped', shipped_at = NOW(), tracking_code = shipments.tracking_code FROM shipments WHERE shipments.order_id = orders.id AND shipments.processed = true AND orders.status = 'processing';
WPM0
Accuracy100%
Progress0%
Streak0🔥
SpeedTarget: 28 WPM
▼
⏱️Start typing...
⚠️Continue typing or resetting in 3...
🏆NEW BEST!
GROUP BY with HAVING
Aggregation with a HAVING filter. The difference between WHERE and HAVING is the concept; typing it cleanly is the challenge.
Part 1 of 1
SELECT category_id, COUNT(*) AS product_count, AVG(price) AS avg_price, MAX(price) AS max_price FROM products WHERE is_active = true GROUP BY category_id HAVING COUNT(*) >= 5 AND AVG(price) < 500 ORDER BY avg_price DESC;
WPM0
Accuracy100%
Progress0%
Streak0🔥
SpeedTarget: 30 WPM
▼
⏱️Start typing...
⚠️Continue typing or resetting in 3...
🏆NEW BEST!
Window Function
ROW_NUMBER and a running total using OVER/PARTITION BY. Window functions have a very specific syntax to get right.
Part 1 of 1
SELECT id, customer_id, total, created_at, ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY created_at DESC) AS order_rank, SUM(total) OVER (PARTITION BY customer_id) AS customer_total FROM orders WHERE status = 'completed';
WPM0
Accuracy100%
Progress0%
Streak0🔥
SpeedTarget: 25 WPM
▼
⏱️Start typing...
⚠️Continue typing or resetting in 3...
🏆NEW BEST!
Common Table Expression (CTE)
A WITH clause CTE followed by a query that uses it. CTEs make complex queries readable but require more to type.
Part 1 of 1
WITH ranked_products AS (SELECT id, name, category_id, price, RANK() OVER (PARTITION BY category_id ORDER BY price DESC) AS price_rank FROM products WHERE is_active = true) SELECT name, price, price_rank FROM ranked_products WHERE price_rank <= 3 ORDER BY category_id, price_rank;
WPM0
Accuracy100%
Progress0%
Streak0🔥
SpeedTarget: 25 WPM
▼
⏱️Start typing...
⚠️Continue typing or resetting in 3...
🏆NEW BEST!
CREATE INDEX
A partial index with a composite key. Index definitions seem short but the syntax needs to be exact.
Part 1 of 1
CREATE INDEX CONCURRENTLY idx_orders_user_status ON orders (user_id, status, created_at DESC) WHERE status IN ('pending', 'processing') AND deleted_at IS NULL;
WPM0
Accuracy100%
Progress0%
Streak0🔥
SpeedTarget: 28 WPM
▼
⏱️Start typing...
⚠️Continue typing or resetting in 3...
🏆NEW BEST!
Stored Function
A PostgreSQL function with parameters and return type. The $$ delimiter and LANGUAGE declaration are easy to fumble.
Part 1 of 1
CREATE OR REPLACE FUNCTION get_user_stats(p_user_id INTEGER) RETURNS TABLE(order_count BIGINT, total_spent NUMERIC) LANGUAGE sql STABLE AS $$ SELECT COUNT(*), COALESCE(SUM(total), 0) FROM orders WHERE user_id = p_user_id AND status = 'completed'; $$;