By Matthew Rathbone · October 23, 2026 · Tip: use your browser's Print (Save as PDF) to keep a copy of this page.

This cheat sheet covers standard SQL, which works in SQLite, PostgreSQL, MySQL and SQL Server unless a note says otherwise. The examples use the small tables from our free lessons (family_members, character and character_actor), so you can paste them into SQL Easy and run them straight away.

The order of a SQL query

You write clauses in this order, and every clause except SELECT and FROM is optional:

SELECT   columns or expressions
FROM     table
JOIN     other_table ON condition
WHERE    row filter
GROUP BY grouping columns
HAVING   group filter
ORDER BY sort columns
LIMIT    row count;

The database runs them in a different order: FROM and JOIN, then WHERE, GROUP BY, HAVING, SELECT, ORDER BY and finally LIMIT. That is why you can't use a SELECT alias inside WHERE, and why WHERE can't filter on a total (that is HAVING's job).

SELECT basics

GoalSyntax
All columnsSELECT * FROM family_members;
Specific columnsSELECT name, species FROM family_members;
Unique valuesSELECT DISTINCT species FROM family_members;
Rename a columnSELECT name AS pet_name FROM family_members;
Rename a tableSELECT f.name FROM family_members AS f;
Sort ascending / descendingORDER BY num_books_read ASC / DESC
First N rowsLIMIT 5 (SQL Server: SELECT TOP 5 ...)
Skip rowsLIMIT 5 OFFSET 10
Comment-- one line or /* many lines */

Practice: SELECT, choosing columns, DISTINCT, ORDER BY, LIMIT, column aliases. More: aliases, comments, TOP.

Filtering with WHERE

OperatorMeaningExample
=, <> (or !=)Equal, not equalWHERE species = 'dog'
>, >=, <, <=ComparisonWHERE num_books_read > 100
AND, OR, NOTCombine conditionsWHERE species = 'human' AND num_books_read > 100
INMatches any in a listWHERE species IN ('dog', 'cat')
BETWEENInclusive rangeWHERE num_books_read BETWEEN 10 AND 50
LIKEPattern matchWHERE name LIKE 'D%'
IS NULL, IS NOT NULLMissing valuesWHERE favorite_book IS NULL

Wildcards for LIKE: % matches any number of characters and _ matches exactly one. Text values use single quotes. NULL never equals anything, not even NULL, so = NULL never matches: use IS NULL.

Practice: WHERE, AND, OR, IN, LIKE, NULL. More: BETWEEN, wildcards, NULL values.

Aggregates and GROUP BY

FunctionReturns
COUNT(*)Number of rows
COUNT(column)Number of non-NULL values
COUNT(DISTINCT column)Number of unique values
SUM(column), AVG(column)Total and average
MIN(column), MAX(column)Smallest and largest
SELECT species, COUNT(*) AS members, AVG(num_books_read) AS avg_books
FROM family_members
GROUP BY species
HAVING COUNT(*) > 1
ORDER BY members DESC;
  • GROUP BY makes one output row per group. Every selected column must be grouped or aggregated.
  • WHERE filters rows before grouping. HAVING filters groups after.

Practice: COUNT, SUM, AVG, MAX and MIN, GROUP BY. More: GROUP BY guide, HAVING guide.

JOINs

A join combines rows from two tables using a matching condition, usually a foreign key pointing at a primary key.

JoinReturns
INNER JOIN (or just JOIN)Only rows that match in both tables
LEFT JOINAll rows from the left table, plus matches from the right (NULL where none)
RIGHT JOINAll rows from the right table, plus matches from the left
FULL OUTER JOINAll rows from both tables, matched where possible
CROSS JOINEvery combination of rows from both tables
Self joinA table joined to itself, using two different aliases
-- Inner join
SELECT character.name, character_actor.actor_name
FROM character
INNER JOIN character_actor
  ON character.id = character_actor.character_id;

-- Left join: keep characters even if they have no actor
SELECT c.name, ca.actor_name
FROM character AS c
LEFT JOIN character_actor AS ca
  ON c.id = ca.character_id;

-- Find rows with no match
SELECT c.name
FROM character AS c
LEFT JOIN character_actor AS ca ON c.id = ca.character_id
WHERE ca.character_id IS NULL;

SQLite only gained RIGHT and FULL OUTER JOIN in version 3.39, and MySQL has no FULL OUTER JOIN. When in doubt, swap the table order and use LEFT JOIN.

Practice: JOIN, multiple joins, LEFT JOIN, self join. More: INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN, joining 3 tables.

UNION, INTERSECT and EXCEPT

Set operators stack the results of two queries that return the same number of compatible columns.

OperatorResult
UNIONRows from both queries, duplicates removed
UNION ALLRows from both queries, duplicates kept (faster)
INTERSECTRows that appear in both
EXCEPTRows in the first query but not the second (Oracle: MINUS)

More: UNION guide.

Common functions

TaskExample
If/else logicCASE WHEN num_books_read > 100 THEN 'bookworm' ELSE 'casual' END
Replace NULLCOALESCE(favorite_book, 'none')
SubstringSUBSTR(name, 1, 3) (SQL Server: SUBSTRING)
Length of textLENGTH(name) (SQL Server: LEN)
Upper / lower caseUPPER(name), LOWER(name)
Trim spacesTRIM(name)
Join textfirst_name || ' ' || last_name (MySQL / SQL Server: CONCAT(...))
Round a numberROUND(price, 2)
Change a typeCAST(price AS INTEGER)

Practice: CASE, SUBSTR, COALESCE. More: CASE guide, ROUND, CAST, concatenating columns.

Window functions

A window function calculates something across a set of related rows without collapsing them into one row, which is how you rank rows or compare a row to the one before it. It always has an OVER clause.

SELECT name, species, num_books_read,
       ROW_NUMBER() OVER (PARTITION BY species ORDER BY num_books_read DESC) AS rank_in_species,
       LAG(num_books_read) OVER (ORDER BY num_books_read) AS previous_value,
       SUM(num_books_read) OVER (PARTITION BY species) AS species_total
FROM family_members;
FunctionWhat it does
ROW_NUMBER()1, 2, 3 ... with no ties
RANK()Ties share a rank, then a gap (1, 1, 3)
DENSE_RANK()Ties share a rank, no gap (1, 1, 2)
LAG(col) / LEAD(col)Value from the previous / next row
SUM(col) OVER (...)Any aggregate as a running or per-group total

PARTITION BY splits rows into groups (like GROUP BY, without collapsing them) and ORDER BY inside OVER sets the order within each group. Window functions need SQLite 3.25+, MySQL 8+, PostgreSQL or SQL Server. More: ROW_NUMBER, DENSE_RANK, LAG and LEAD, PARTITION BY.

Subqueries and CTEs

-- Subquery in WHERE
SELECT name FROM family_members
WHERE num_books_read > (SELECT AVG(num_books_read) FROM family_members);

-- Common table expression (CTE): a named, reusable subquery
WITH readers AS (
  SELECT species, SUM(num_books_read) AS total
  FROM family_members
  GROUP BY species
)
SELECT * FROM readers WHERE total > 100;

Practice: nested queries.

INSERT, UPDATE and DELETE

GoalSyntax
Add a rowINSERT INTO family_members (name, species) VALUES ('Rex', 'dog');
Change rowsUPDATE family_members SET num_books_read = 5 WHERE name = 'Rex';
Delete rowsDELETE FROM family_members WHERE name = 'Rex';
Empty a tableTRUNCATE TABLE family_members; (not in SQLite: use DELETE FROM)

Always include a WHERE on UPDATE and DELETE unless you really mean every row. Run the same WHERE in a SELECT first to see which rows you are about to change. More: INSERT INTO, UPDATE, DELETE, TRUNCATE vs DELETE vs DROP.

Creating and changing tables

CREATE TABLE pets (
  id         INTEGER PRIMARY KEY,
  name       TEXT NOT NULL,
  species    TEXT DEFAULT 'dog',
  owner_id   INTEGER REFERENCES owners (id),
  email      TEXT UNIQUE
);

ALTER TABLE pets ADD COLUMN age INTEGER;
ALTER TABLE pets RENAME COLUMN name TO pet_name;
ALTER TABLE pets DROP COLUMN age;       -- SQLite 3.35+, PostgreSQL, MySQL, SQL Server

CREATE INDEX idx_pets_owner ON pets (owner_id);
CREATE VIEW dogs AS SELECT * FROM pets WHERE species = 'dog';

DROP TABLE pets;
ConstraintRule it enforces
PRIMARY KEYUnique, non-null identifier for each row
FOREIGN KEY / REFERENCESValue must exist in another table's key
NOT NULLThe column can't be empty
UNIQUENo duplicate values
DEFAULTValue used when none is given
CHECKValue must satisfy a condition

Common data types: INTEGER, DECIMAL(p, s), VARCHAR(n) or TEXT, DATE, TIMESTAMP / DATETIME, BOOLEAN. Exact names vary by database. More: CREATE TABLE, ALTER, rename a column, drop a column, primary keys, indexes, views.

DDL, DML, DQL and DCL

You'll hear SQL commands grouped by what they do:

GroupStands forCommands
DQLData Query LanguageSELECT
DMLData Manipulation LanguageINSERT, UPDATE, DELETE
DDLData Definition LanguageCREATE, ALTER, DROP, TRUNCATE
DCLData Control LanguageGRANT, REVOKE
TCLTransaction Control LanguageBEGIN, COMMIT, ROLLBACK

Details in What are DDL, DML, DQL and DCL?

Differences between databases

The core is identical everywhere. The differences are mostly in the small functions:

TaskSQLitePostgreSQLMySQLSQL Server
Limit rowsLIMIT nLIMIT nLIMIT nTOP n or OFFSET ... FETCH
Today's datedate('now')CURRENT_DATECURDATE()CAST(GETDATE() AS DATE)
Concatenate||||CONCAT()CONCAT() or +
Text lengthLENGTH()LENGTH()LENGTH() (bytes) / CHAR_LENGTH()LEN()
Auto-increment keyINTEGER PRIMARY KEYGENERATED ... AS IDENTITY or SERIALAUTO_INCREMENTIDENTITY(1,1)
Quote an identifier"name""name"`name`[name]

Date handling differs the most. See our guides for MySQL, PostgreSQL, SQL Server (T-SQL) and SQLite.

Common mistakes this cheat sheet will save you from

  • WHERE column = NULL never matches. Use IS NULL.
  • WHERE COUNT(*) > 1 is an error. Use HAVING COUNT(*) > 1.
  • Selecting a column that isn't in GROUP BY or an aggregate.
  • Double quotes for text. Use single quotes for values: 'dog'.
  • UPDATE or DELETE with no WHERE.
  • A JOIN with no ON condition, which multiplies rows.

Learn it by doing, not by reading

A cheat sheet helps you remember syntax you've already met. It won't teach you to think in SQL. For that you need to write queries and see results. SQL Easy has 31 short, free lessons, from your first SELECT to LEFT JOIN and CASE, that run in your browser with nothing to install and no signup. When you're ready to query your own data, Beekeeper Studio is a free, open source SQL editor for PostgreSQL, MySQL, SQLite and SQL Server. Not sure where to begin? Read Is SQL easy to learn?

Frequently asked questions

What are the most important SQL commands to learn first?

SELECT, FROM, WHERE, ORDER BY, GROUP BY and JOIN cover most day-to-day queries. After those, learn INSERT, UPDATE and DELETE for changing data, and CREATE TABLE for building it.

Is there a downloadable SQL cheat sheet PDF?

Use your browser's Print function on this page and choose "Save as PDF". You get an up-to-date copy that includes every table and example above.

Does this cheat sheet work for MySQL, PostgreSQL and SQL Server?

Yes. The queries in the sections above are standard SQL and run on all of them. Where syntax differs (limiting rows, dates, string functions), the notes and the comparison table call it out.

What is the difference between WHERE and HAVING?

WHERE filters individual rows before grouping, so it can't use aggregates like COUNT(*). HAVING filters groups after GROUP BY, so it can.

What is the difference between INNER JOIN and LEFT JOIN?

INNER JOIN keeps only rows with a match in both tables. LEFT JOIN keeps every row from the left table and fills the right-hand columns with NULL when there is no match.