SQL Cheat Sheet: Commands, Clause Order and Dialects

· 9 MIN

TL;DR: This SQL cheat sheet puts the commands you look up most in one table, then shows the order SQL actually runs clauses in, joins, aggregates, window functions and CTEs. Where PostgreSQL, MySQL and SQL Server disagree (row limits, upserts, string matching), the syntax is shown side by side so you can copy the version your database accepts.

A SQL cheat sheet is a one-page reference of SQL statement syntax. The core SQL commands cheat sheet fits in five groups: querying (SELECT, WHERE, GROUP BY, HAVING, ORDER BY), changing data (INSERT, UPDATE, DELETE, MERGE), defining structure (CREATE, ALTER, DROP), controlling access (GRANT, REVOKE) and transactions (BEGIN, COMMIT, ROLLBACK). Most mistakes come from two things this page covers early: the order clauses are evaluated in, and the dialect differences between PostgreSQL, MySQL and SQL Server.

SQL statement cheat sheet: the lookup table

TaskSyntaxNotes
Select columnsSELECT a, b FROM t;SELECT * only for exploring
Remove duplicatesSELECT DISTINCT a FROM t;Applies to the whole row selected
Filter rowsWHERE amount > 100 AND status = 'paid'Runs before grouping
Pattern matchWHERE email LIKE '%@acme.com'% any chars, _ one char
List / rangeWHERE id IN (1,2,3), BETWEEN 1 AND 10BETWEEN is inclusive
Null checkWHERE deleted_at IS NULL= NULL is never true
SortORDER BY created_at DESCOnly guaranteed order
AggregateCOUNT(*), SUM(x), AVG(x), MIN, MAXCOUNT(col) skips NULLs
GroupGROUP BY country HAVING COUNT(*) > 5HAVING filters groups
JoinFROM a JOIN b ON b.a_id = a.idSee joins section
Combine resultsUNION, UNION ALL, INTERSECT, EXCEPTUNION removes duplicates
InsertINSERT INTO t (a, b) VALUES (1, 'x');Multi-row VALUES works everywhere listed
UpdateUPDATE t SET a = 2 WHERE id = 7;Forget WHERE and every row changes
DeleteDELETE FROM t WHERE id = 7;TRUNCATE empties the table
Create tableCREATE TABLE t (id INT PRIMARY KEY, ...);Add NOT NULL, UNIQUE, FOREIGN KEY
Change tableALTER TABLE t ADD COLUMN c TEXT;
IndexCREATE INDEX idx_t_a ON t (a);Speeds WHERE/JOIN on a
TransactionBEGIN; ... COMMIT; or ROLLBACK;SQL Server: BEGIN TRANSACTION

What are the basic SQL commands?

The basic SQL commands are SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER and DROP. They split into DQL/DML (reading and changing rows) and DDL (changing structure), with GRANT/REVOKE (DCL) and COMMIT/ROLLBACK (TCL) completing the set. A typical analytical query uses only SELECT with its clauses:

SELECT country, COUNT(*) AS customers, SUM(mrr) AS total_mrr
FROM customers
WHERE status = 'active'
GROUP BY country
HAVING SUM(mrr) > 1000
ORDER BY total_mrr DESC
LIMIT 10;

What is the order of SQL clauses?

SQL is written in one order and evaluated in another. You write SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT, but the database logically processes them like this:

StepClauseWhat it does
1FROM / JOINBuilds the row set
2WHEREDrops rows
3GROUP BYCollapses rows into groups
4HAVINGDrops groups
5SELECTComputes output columns and aliases; window functions run here
6DISTINCTRemoves duplicate output rows
7ORDER BYSorts
8LIMIT / OFFSET / TOPCuts the result
SQL evaluates FROM first and the row limit last, regardless of written order
FIG 01 — Aliases defined in SELECT are not visible to WHERE.

This explains the most common error: you cannot use a SELECT alias in WHERE, because WHERE runs first. ORDER BY can use the alias because it runs after. It also explains why you filter aggregates with HAVING, not WHERE. For a deeper look at filtering, see our guide to CASE WHEN in PostgreSQL.

Joins cheat sheet

JoinReturns
INNER JOINRows with a match in both tables
LEFT JOINAll left rows, NULLs where the right has no match
RIGHT JOINAll right rows (rewrite as LEFT for readability)
FULL OUTER JOINAll rows from both sides; not in MySQL
CROSS JOINEvery combination of rows
Self joinA table joined to itself with two aliases
-- customers with no orders
SELECT c.id, c.name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id IS NULL;
Ana has two orders so she appears twice in the join, Ben once
FIG 02 — A join returns one row per match, so totals can double count.

MySQL has no FULL OUTER JOIN; emulate it with a LEFT JOIN UNION a RIGHT JOIN. Joining when you only need columns from several tables without a key is covered in selecting from multiple tables without JOIN.

What differs between MySQL and PostgreSQL syntax?

The core of SQL is portable; row limits, upserts, string handling and identifiers are not. These are the differences you hit most:

TaskPostgreSQLMySQLSQL Server
First 10 rowsLIMIT 10LIMIT 10SELECT TOP (10) or OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
Skip 20, take 10LIMIT 10 OFFSET 20LIMIT 20, 10 or LIMIT 10 OFFSET 20OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
UpsertINSERT ... ON CONFLICT (id) DO UPDATEINSERT ... ON DUPLICATE KEY UPDATEMERGE
Case-insensitive matchILIKELIKE (default collations are case-insensitive)LIKE (depends on collation)
Concatenatea \\b or CONCAT(a, b)CONCAT(a, b)a + b or CONCAT(a, b)
Quote identifiers"column"` column[column]`
Return changed rowsRETURNING *not supportedOUTPUT inserted.*
Current timenow()NOW()GETDATE() / SYSDATETIME()
Auto-incrementGENERATED ALWAYS AS IDENTITYAUTO_INCREMENTIDENTITY(1,1)
-- PostgreSQL upsert
INSERT INTO plans (id, price) VALUES (1, 30)
ON CONFLICT (id) DO UPDATE SET price = EXCLUDED.price
RETURNING *;

-- MySQL upsert
INSERT INTO plans (id, price) VALUES (1, 30)
ON DUPLICATE KEY UPDATE price = 30;

Version notes worth knowing: ILIKE is a PostgreSQL extension, not standard SQL (PostgreSQL docs). PostgreSQL got MERGE in version 15 (release notes). In SQL Server, TOP ... WITH TIES requires ORDER BY and can return more rows than asked for when the last value ties (Microsoft Learn). In every dialect, a row limit without ORDER BY returns an unpredictable subset, and rows skipped by a large OFFSET are still computed by the server (PostgreSQL docs). Date functions differ most of all; see GETDATE in PostgreSQL and DATEDIFF in PostgreSQL.

How do window functions work?

A window function computes a value for each row from a set of related rows (the window) without collapsing them the way GROUP BY does. You define the window with OVER (PARTITION BY ... ORDER BY ...). They are supported in PostgreSQL, MySQL 8.0 and later, SQL Server and SQLite 3.25.0 and later (SQLite docs).

FunctionReturns
ROW_NUMBER()1, 2, 3 per partition, no ties
RANK() / DENSE_RANK()Rank with gaps / without gaps
LAG(x) / LEAD(x)Previous / next row's value
SUM(x) OVER (ORDER BY d)Running total
NTILE(4)Quartile bucket
-- latest order per customer
SELECT *
FROM (
  SELECT o.*, ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY created_at DESC) AS rn
  FROM orders o
) x
WHERE rn = 1;

Window functions run at the SELECT step, so you cannot filter on them in WHERE; wrap the query as above. More patterns in our PARTITION BY guide.

CTEs (WITH queries)

A common table expression is a named subquery defined with WITH that exists for one statement. It makes multi-step logic readable, and WITH RECURSIVE walks hierarchies such as org charts or category trees.

WITH monthly AS (
  SELECT date_trunc('month', created_at) AS month, SUM(amount) AS revenue
  FROM orders
  GROUP BY 1
)
SELECT month, revenue,
       revenue - LAG(revenue) OVER (ORDER BY month) AS change
FROM monthly
ORDER BY month;

In PostgreSQL a non-recursive CTE referenced once is inlined into the main query; add MATERIALIZED or NOT MATERIALIZED after AS to override that (PostgreSQL docs). MySQL supports CTEs from 8.0. date_trunc is PostgreSQL; in MySQL use DATE_FORMAT(created_at, '%Y-%m-01').

Common mistakes the cheat sheet saves you from

When you'd rather not write the query

A cheat sheet helps when you know SQL and forget syntax. When the person with the question is in ops, sales or finance, the bottleneck is the query itself. AI for Database connects to PostgreSQL, MySQL, SQL Server and other sources, answers plain-English questions with the SQL it ran shown next to the result, and can pin the answer to a self-refreshing dashboard or an alert. Try AI for Database free at aifordatabase.com, sample data included.

Frequently Asked Questions

What are the basic SQL commands?

SELECT reads rows, INSERT adds them, UPDATE changes them, DELETE removes them, and CREATE, ALTER and DROP manage tables and indexes. GRANT/REVOKE control access and COMMIT/ROLLBACK end transactions.

What is the order of SQL clauses?

Written order is SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT. Logical order is FROM/JOIN, WHERE, GROUP BY, HAVING, SELECT, DISTINCT, ORDER BY, LIMIT, which is why WHERE cannot see SELECT aliases.

How do window functions work?

They compute a value per row across a window defined by OVER (PARTITION BY ... ORDER BY ...) while keeping every row, unlike GROUP BY. Use them for rankings, running totals and previous-row comparisons.

What differs between MySQL and PostgreSQL syntax?

Mainly upserts (ON DUPLICATE KEY UPDATE vs ON CONFLICT), case-insensitive matching (LIKE collation vs ILIKE), identifier quoting (backticks vs double quotes), RETURNING (PostgreSQL only), FULL OUTER JOIN (PostgreSQL only) and date functions.

What is the difference between WHERE and HAVING?

WHERE filters individual rows before grouping; HAVING filters groups after aggregation. HAVING SUM(amount) > 100 works, WHERE SUM(amount) > 100 is an error.

Is SQL the same in every database?

The ANSI core (SELECT, joins, GROUP BY, CTEs, window functions) is shared. Row limits, upserts, string and date functions, and procedural code differ by vendor, so check the dialect table above before copying a query between databases.

Ready to try AI for Database?

Query your database in plain English. No SQL required. Start free today.