SQL Syntax Cheat Sheet: Joins, Clauses, and Common Functions
A quick-reference SQL syntax cheat sheet — clause write order vs execution order, join types, aggregate/string/date functions, and CRUD skeletons.
Try it now: SQL Formatter — Format and beautify SQL for PostgreSQL, MySQL and SQL Server — clauses on their own lines, subqueries indented, literals untouched.
Clause Order: Written vs. Executed
The single most useful fact on this sql cheat sheet: the order you write clauses in is not the order the database actually runs them in. That mismatch is exactly why WHEREcan't reference a SELECT alias but ORDER BY can — by the time ORDER BY runs, the alias already exists; by the time WHEREruns, it doesn't.
| Written order | Logical execution order |
|---|---|
| 1. SELECT | 1. FROM / JOIN |
| 2. FROM | 2. WHERE |
| 3. WHERE | 3. GROUP BY |
| 4. GROUP BY | 4. HAVING |
| 5. HAVING | 5. SELECT |
| 6. ORDER BY | 6. ORDER BY |
| 7. LIMIT | 7. LIMIT |
Same reasoning explains why HAVING filters on aggregates and WHEREcan't —WHERE runs before GROUP BY has produced any aggregates to filter on. Full clause precision and dialect-specific edge cases are covered in the SQL Formatter guide.
Join Types
| Join | Returns | Example |
|---|---|---|
INNER JOIN | Only rows with a match on both sides | FROM orders o INNER JOIN customers c ON c.id = o.customer_id |
LEFT JOIN | All rows from the left table; unmatched right-side columns are NULL | FROM customers c LEFT JOIN orders o ON o.customer_id = c.id |
RIGHT JOIN | All rows from the right table; unmatched left-side columns are NULL | FROM orders o RIGHT JOIN customers c ON c.id = o.customer_id |
FULL OUTER JOIN | All rows from both sides; unmatched columns on either side are NULL | FROM a FULL OUTER JOIN b ON a.id = b.a_id |
CROSS JOIN | Every row from the left paired with every row from the right — no ON clause | FROM sizes CROSS JOIN colors |
A LEFT JOIN silently turns into an INNER JOIN the moment a WHERE clause filters on a nullable right-side column — a bug common enough to have its own section in the EXPLAIN ANALYZE guide.
Aggregate Functions
| Function | Description | Example |
|---|---|---|
COUNT(*) | Number of rows in the group | SELECT COUNT(*) FROM orders |
SUM(col) | Total of a numeric column | SELECT SUM(total) FROM orders |
AVG(col) | Mean of a numeric column | SELECT AVG(total) FROM orders |
MIN(col) | Smallest value | SELECT MIN(created_at) FROM orders |
MAX(col) | Largest value | SELECT MAX(created_at) FROM orders |
All five ignore NULL values except COUNT(*), which counts rows regardless of nulls — COUNT(col) counts only the non-null values of that column.
String Functions
| Function | Description | Example |
|---|---|---|
CONCAT(a, b) | Join strings together | CONCAT(first_name, ' ', last_name) |
UPPER(s) / LOWER(s) | Change case | UPPER(email) |
TRIM(s) | Strip leading/trailing whitespace | TRIM(username) |
SUBSTRING(s, start, len) | Extract part of a string | SUBSTRING(sku, 1, 3) |
LENGTH(s) | Character count | LENGTH(description) |
These names are not universal — this is the sql functions list most engines agree on closely enough to be useful, not a guarantee. Postgres also accepts || for concatenation alongside CONCAT(); SQL Server uses LEN() instead of LENGTH(); MySQL's CONCAT() tolerates NULL arguments where Postgres's || propagates them. Check your specific engine before assuming a name works everywhere.
Date Functions
| Function (Postgres) | MySQL equivalent | SQL Server equivalent |
|---|---|---|
NOW() | NOW() | GETDATE() |
CURRENT_DATE | CURDATE() | CAST(GETDATE() AS DATE) |
date_col + interval '1 day' | DATE_ADD(date_col, INTERVAL 1 DAY) | DATEADD(day, 1, date_col) |
date_part('year', date_col) | YEAR(date_col) | YEAR(date_col) |
Date functions vary more across engines than almost anything else in SQL — treat this table as a starting point for translating a query between dialects, not as syntax you can paste unchanged into any database.
CRUD Statement Skeletons
SELECT column1, column2 FROM table_name WHERE condition GROUP BY column1 HAVING aggregate_condition ORDER BY column1 LIMIT 10;
INSERT INTO table_name (column1, column2) VALUES (value1, value2);
UPDATE table_name SET column1 = value1 WHERE condition;
DELETE FROM table_name WHERE condition;
An UPDATE or DELETE with no WHERE clause runs against every row in the table — worth a second look before you hit execute. Building these safely, with real placeholders instead of string-concatenated values, is what the parameterized query builder guide covers.
Formatting the Query You Just Wrote
Writing correct SQL and writing SQL a reviewer can read in a pull request are two different jobs. Once a query works, running it through GenKitLab's SQL Formatter puts each clause on its own line and indents subqueries consistently for Postgres, MySQL or SQL Server — the same before/after transform covered in the SQL Formatter guide. It runs entirely in your browser, so nothing you paste is uploaded anywhere.
Frequently asked questions
›What is the correct order of SQL clauses?
Written order is SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT. Logical execution order is different: FROM/JOIN, WHERE, GROUP BY, HAVING, SELECT, ORDER BY, LIMIT. The mismatch between the two is why a WHERE clause can't reference a column alias defined in SELECT, but ORDER BY can.
›What's the difference between INNER JOIN and LEFT JOIN?
INNER JOIN returns only rows that have a match on both sides. LEFT JOIN returns every row from the left table regardless of a match, filling unmatched right-side columns with NULL — which is also why a WHERE clause that filters on one of those nullable columns can accidentally turn a LEFT JOIN back into an INNER JOIN.
›Why can't I use a SELECT alias in a WHERE clause?
Because of execution order, not written order: WHERE runs before SELECT does, so the alias doesn't exist yet when WHERE is evaluated. ORDER BY runs after SELECT, which is why the same alias works fine there.
›Are SQL string and date functions the same across Postgres, MySQL and SQL Server?
No — this varies more than most people expect. Postgres accepts || for string concatenation alongside CONCAT(); SQL Server uses LEN() instead of LENGTH(); date arithmetic syntax (DATEADD vs DATE_ADD vs interval math) differs across all three. Always confirm the exact function name for your specific engine rather than assuming this cheat sheet's names are universal.
›What does HAVING do that WHERE can't?
HAVING filters on the result of an aggregate function (like COUNT(*) or SUM(total)) after GROUP BY has run. WHERE runs before GROUP BY, so at that point no aggregates exist yet to filter on — that's the entire reason HAVING exists as a separate clause.
›What happens if I run UPDATE or DELETE without a WHERE clause?
It applies to every row in the table — there's no implicit safety limit. Double-checking the WHERE clause before executing an UPDATE or DELETE, especially against a production database, is worth the extra few seconds every time.
Last updated