Start.dev
Back to blog

SQL for beginners: the 10 queries to learn first

A practical guide to the ten SQL queries that cover most of what beginners need: SELECT, WHERE, ORDER BY, GROUP BY, JOIN, and safe UPDATE and DELETE.

Ariel Weinberger
Ariel Weinberger ยท 4 min read

Most SQL tutorials start with theory: relational algebra, normal forms, a tour of every keyword. You don't need any of that on day one.

A small set of queries does the heavy lifting in real projects. Learn these ten, in this order, and you can answer most of the questions people actually ask a database.

All the examples use two tables from a made-up online shop:

customers(id, name, country)
orders(id, customer_id, total, status, ordered_at)

Each customer can have many orders, and orders.customer_id points back to customers.id. The syntax works in PostgreSQL, SQLite and MySQL unless noted.

1. SELECT: look at your data

Every read starts here. Pick the columns you want and the table they live in.

SELECT name, country
FROM customers;

SELECT * returns every column, which is fine for poking around. In code you want to ask for only what you need.

Want nicer column names in the output? Rename them with AS:

SELECT name AS customer_name, country
FROM customers;

2. WHERE: filter rows

WHERE keeps only the rows that match a condition.

SELECT id, total
FROM orders
WHERE status = 'shipped';

Text goes in single quotes. Numbers don't. Comparisons you'll use constantly: =, <> (not equal), <, >, <=, >=.

3. AND, OR, IN, LIKE and NULL: filter smarter

Real questions have more than one condition.

SELECT id, total, status
FROM orders
WHERE total > 100
  AND (status = 'shipped' OR status = 'delivered');

The parentheses matter. AND binds tighter than OR, so without them you get a different answer.

Two shortcuts make this tidier:

-- Match any value in a list
SELECT * FROM orders WHERE status IN ('shipped', 'delivered');
 
-- Partial text match: names starting with "A"
SELECT * FROM customers WHERE name LIKE 'A%';

Then there's the classic trap: missing values. NULL means "unknown", and nothing equals it, not even another NULL. Use IS NULL:

SELECT * FROM customers WHERE country IS NULL;   -- works
SELECT * FROM customers WHERE country = NULL;    -- returns nothing, ever

4. ORDER BY and LIMIT: sort and trim

Tables have no guaranteed order. If order matters, ask for it.

SELECT id, total
FROM orders
ORDER BY total DESC
LIMIT 5;

That's your five biggest orders. ASC is the default direction, DESC reverses it. You can sort by several columns: ORDER BY country, name.

5. COUNT, SUM, AVG, MIN, MAX: summarize

Aggregate functions collapse many rows into one answer.

SELECT
  COUNT(*)   AS order_count,
  SUM(total) AS revenue,
  AVG(total) AS average_order,
  MAX(total) AS biggest_order
FROM orders;

COUNT(*) counts rows. COUNT(column) counts only the rows where that column isn't NULL, a difference that surprises people.

6. GROUP BY: summarize per category

Totals are useful. Totals per customer, per country or per month are where it gets interesting.

SELECT customer_id, COUNT(*) AS orders, SUM(total) AS spent
FROM orders
GROUP BY customer_id;

The rule of thumb: every column in SELECT must either be in the GROUP BY or wrapped in an aggregate function.

7. HAVING: filter the groups

WHERE filters rows before grouping. HAVING filters the groups after.

SELECT customer_id, SUM(total) AS spent
FROM orders
GROUP BY customer_id
HAVING SUM(total) > 500
ORDER BY spent DESC;

This returns only customers who've spent more than 500, biggest spenders first. If you try this condition in WHERE, the database will complain, because the sum doesn't exist yet at that stage.

8. JOIN: combine tables

Orders are only useful if you know whose they are. JOIN stitches two tables together on a shared column.

SELECT customers.name, orders.id, orders.total
FROM orders
JOIN customers ON customers.id = orders.customer_id;

Short aliases keep this readable once queries grow:

SELECT c.name, o.id, o.total
FROM orders AS o
JOIN customers AS c ON c.id = o.customer_id;

A plain JOIN (an inner join) returns only rows that have a match on both sides. For the other join types, with a worked example of each, see SQL Joins Explained with Simple Examples.

9. LEFT JOIN: keep the unmatched rows

What if you want every customer, including the ones who never ordered? An inner join drops them. A LEFT JOIN keeps everything from the left table and fills the gaps with NULL.

SELECT c.name, COUNT(o.id) AS orders
FROM customers AS c
LEFT JOIN orders AS o ON o.customer_id = c.id
GROUP BY c.id, c.name
ORDER BY orders ASC;

Customers with zero orders now show up with a count of 0. This pattern, "find the things with none of the other thing", is one of the most useful in all of SQL. We take it apart step by step in our guide to SQL joins.

10. INSERT, UPDATE and DELETE: change data safely

The first nine queries only read. The last one writes, and writing is where mistakes get expensive.

INSERT INTO customers (name, country)
VALUES ('Ada', 'UK');
 
UPDATE orders
SET status = 'cancelled'
WHERE id = 42;
 
DELETE FROM orders
WHERE id = 42;

The most important habit in this whole article: always write the WHERE. An UPDATE or DELETE without one touches every row in the table.

A safe routine:

  1. Write the WHERE as a SELECT first and check the rows it returns.
  2. Swap SELECT * for UPDATE ... SET or DELETE.
  3. Run it inside a transaction (BEGIN; ... COMMIT;) if your database supports it, so you can ROLLBACK if the result looks wrong.

Postgres and SQLite also support RETURNING, which shows you the rows you just changed: UPDATE orders SET status = 'cancelled' WHERE id = 42 RETURNING *;

The order SQL actually runs in

You write SELECT first, but the database doesn't run it first. Roughly, the order is:

  1. FROM and JOIN: find and combine the tables
  2. WHERE: filter rows
  3. GROUP BY: form groups
  4. HAVING: filter groups
  5. SELECT: compute the output columns
  6. ORDER BY and LIMIT: sort and trim

This explains most beginner errors. You can't use a SELECT alias inside WHERE, for example, because WHERE runs before the alias exists.

How to practice

Reading about queries only gets you so far. Type them out, break them, and see what the database says. Start with one table, ask ten small questions, then add the second table and repeat.

If you want a guided path through exactly these topics, the Learn SQL (opens in a new tab) course walks through them with runnable exercises. These lessons map directly to the queries above:

Ten queries won't make you a database expert, but they will let you answer real questions with real data. That's the point where SQL stops feeling like a syllabus and starts feeling like a tool.

Level up your coding skills

Jump into interactive courses and start building real-world projects.

Start learning

Related posts

Confused by SQL joins? Learn INNER, LEFT, RIGHT, FULL and CROSS joins with one tiny dataset, plain-English explanations and copy-paste queries.
Ariel Weinberger
Ariel Weinberger ยท 7 min read
Build your first REST API with Node.js and Express in about 50 lines: routes, JSON, POST, PUT and DELETE, tested with curl, plus where to go next.
Ariel Weinberger
Ariel Weinberger ยท 5 min read