SQL Cheat Sheet
Reference · Updated August 26, 2026
Everything on this page is standard SQL syntax you'll meet in almost any relational database (MySQL, PostgreSQL, SQL Server, SQLite). It's organized the way you actually reach for it while writing a query — not alphabetically. Every example runs as-is against the sample tables in the Playground, so paste one in and see the result instead of taking it on faith.
The order SQL actually runs in
This is the single most useful thing to memorize, because it explains half of SQL's "weird" rules — like why you can't filter on a column alias in WHERE, or use COUNT() there either. You write a query top to bottom, but the database executes it in this order:
| Step | Clause | What happens |
|---|---|---|
| 1 | FROM / JOIN | Tables are combined into one working set of rows. |
| 2 | WHERE | Individual rows are filtered. No aggregates or aliases exist yet. |
| 3 | GROUP BY | Remaining rows are collapsed into groups. |
| 4 | HAVING | Groups are filtered — this is where aggregate conditions belong. |
| 5 | SELECT | Output columns and aliases are computed. |
| 6 | ORDER BY | The result is sorted. Aliases from SELECT can be used here. |
| 7 | LIMIT / OFFSET | The final row count is trimmed. |
SELECT basics
| Syntax | Does |
|---|---|
| SELECT * | Every column. |
| SELECT a, b | Only the named columns, in that order. |
| SELECT a AS x | Renames a column in the output. |
| SELECT DISTINCT a | Removes duplicate rows from the result. |
| ORDER BY a DESC | Sorts by column a, highest first (ASC is the default). |
| LIMIT 10 | Returns at most 10 rows. |
SELECT name, unit_price
FROM Products
ORDER BY unit_price DESC
LIMIT 3;
WHERE operators
| Operator | Example | Matches |
|---|---|---|
| = <> > < >= <= | unit_price > 100 | Standard comparisons. |
| AND / OR / NOT | a = 1 AND b = 2 | Combine conditions. Parenthesize when mixing AND/OR. |
| IN (...) | tier IN ('Gold','Platinum') | Any value in the list. Shorthand for chained ORs. |
| BETWEEN a AND b | price BETWEEN 10 AND 50 | Inclusive of both endpoints. |
| LIKE | name LIKE 'A%' | % = any characters, _ = exactly one character. |
| IS NULL / IS NOT NULL | manager_id IS NULL | Missing values. = NULL never matches anything. |
JOINs
| Type | Returns |
|---|---|
| INNER JOIN | Only rows that match in both tables. The default when you write plain JOIN. |
| LEFT JOIN | Every row from the left table, matched data where it exists, NULLs where it doesn't. |
| RIGHT JOIN | The mirror of LEFT JOIN — every row from the right table. |
| FULL JOIN | Every row from both sides, matched where possible, NULLs where not. |
SELECT e.first_name, d.dept_name
FROM Employees e
LEFT JOIN Departments d ON e.dept_id = d.dept_id;
Aggregate functions
| Function | Returns |
|---|---|
| COUNT(*) | Number of rows (COUNT(col) skips NULLs in that column). |
| SUM(col) | Total of a numeric column. |
| AVG(col) | Average of a numeric column. |
| MIN(col) / MAX(col) | Smallest / largest value. |
| ROUND(n, d) | Rounds n to d decimal places. Often wraps AVG() or SUM(). |
Every aggregate collapses a set of rows into one value — used alone, that means the whole table; used with GROUP BY, one value per group.
SELECT dept_id, COUNT(*) AS headcount, ROUND(AVG(salary), 0) AS avg_salary
FROM Employees
GROUP BY dept_id
HAVING COUNT(*) > 2
ORDER BY headcount DESC;
Subqueries
| Form | Use |
|---|---|
| WHERE col = (SELECT ...) | Scalar subquery — returns exactly one value, compared directly. |
| WHERE col IN (SELECT ...) | Returns a column of values to test membership against. |
| FROM (SELECT ...) AS t | A subquery used as a table — must be aliased. |
SELECT first_name, salary
FROM Employees
WHERE salary > (SELECT AVG(salary) FROM Employees);
CASE — conditional values
SELECT order_id, order_total,
CASE
WHEN order_total >= 8000 THEN 'Large'
WHEN order_total >= 3000 THEN 'Medium'
ELSE 'Small'
END AS size_band
FROM Orders;
CASE checks its WHEN branches top to bottom and stops at the first match — always include an ELSE, or unmatched rows come back NULL.
Changing data
| Statement | Does |
|---|---|
| INSERT INTO t (a,b) VALUES (1,2) | Adds a new row. |
| UPDATE t SET a=1 WHERE id=5 | Changes existing rows. Omit WHERE and every row changes. |
| DELETE FROM t WHERE id=5 | Removes rows. Omit WHERE and the table empties. |
The WHERE clause is doing the same job here as in SELECT — it's just as easy to forget, and far more expensive to forget in an UPDATE or DELETE. It's safe to experiment with all three in the Playground: the sample database resets to its original state on every page refresh.
Put it into practice
Reading a cheat sheet and writing a query from scratch are different skills. The Practice section has 28 exercises that check your answer by running it, not by matching text.
Start practising