Reference · every clause and function you'll actually be tested on

SQL Cheatsheet

Every example below runs against the same two tables, so you can see how clauses actually chain together instead of reading isolated snippets.

employees(id, name, dept, salary, manager_id, hire_date)  ·  orders(id, employee_id, product, amount, order_date)
01

Clause execution order

SQL is written SELECT-first but executed FROM-first. This is the single most useful mental model in the whole language — it explains why you can't filter on an alias in WHERE but can in HAVING, and why aggregates aren't allowed in WHERE.

FROMbuild source rows JOINattach related rows WHEREfilter raw rows GROUP BYcollapse into groups HAVINGfilter groups SELECTcompute columns ORDER BYsort result LIMITcut to N rows
Why it matters: WHERE runs before SELECT exists, so it can't see column aliases or aggregate results — that's what HAVING is for. ORDER BY runs last, so it's the one clause that can reference a SELECT alias.
02

Filtering & sorting

The clauses that shape which rows come back and in what order.

WHEREfilter
Keeps rows matching a condition, evaluated before grouping.
SELECT * FROM employees
WHERE salary > 60000;
DISTINCTdedupe
Collapses duplicate rows in the result set.
SELECT DISTINCT dept
FROM employees;
ORDER BYsort
Sorts final rows; add DESC for descending, list multiple columns for tie-breaks.
SELECT name, salary FROM employees
ORDER BY salary DESC, name ASC;
LIMIT / OFFSETpage
Caps row count; OFFSET skips rows — the pair behind every pagination query.
SELECT * FROM employees
ORDER BY salary DESC
LIMIT 10 OFFSET 20;
BETWEENrange
Inclusive range check — equivalent to two >=/<= conditions.
SELECT * FROM employees
WHERE salary BETWEEN 50000 AND 90000;
INset
Matches against a fixed list or a subquery result.
SELECT * FROM employees
WHERE dept IN ('Sales','Eng');
LIKEpattern
% = any run of characters, _ = exactly one character.
SELECT * FROM employees
WHERE name LIKE 'A%';    -- starts with A
IS NULLnull-check
NULL is never equal to anything, not even itself — = NULL silently matches zero rows.
SELECT * FROM employees
WHERE manager_id IS NULL;
AND / OR / NOTlogic
Combine conditions; parenthesize when mixing AND with OR — precedence bugs live here.
WHERE dept = 'Eng'
  AND (salary > 80000 OR manager_id IS NULL);
03

Joins

Joins decide which rows from each side survive when the two tables are matched on a key. Picking the wrong one is the most common cause of a "why are rows missing" bug.

JoinKeepsQuery
INNER JOINOnly rows with a match on both sides.SELECT e.name, o.product FROM employees e INNER JOIN orders o ON e.id = o.employee_id;
LEFT JOINAll rows from the left table; unmatched right columns come back NULL.SELECT e.name, o.product FROM employees e LEFT JOIN orders o ON e.id = o.employee_id;
RIGHT JOINAll rows from the right table; unmatched left columns come back NULL.SELECT e.name, o.product FROM employees e RIGHT JOIN orders o ON e.id = o.employee_id;
FULL OUTER JOINEvery row from both sides; gaps on either side filled with NULL.SELECT e.name, o.product FROM employees e FULL OUTER JOIN orders o ON e.id = o.employee_id;
CROSS JOINEvery row of the left paired with every row of the right (Cartesian product) — no ON clause.SELECT * FROM employees CROSS JOIN orders;
Self joinA table joined to itself — the standard way to look up a manager's own record.SELECT e.name AS emp, m.name AS manager FROM employees e LEFT JOIN employees m ON e.manager_id = m.id;
Rule of thumb: if you're not sure whether unmatched rows should disappear or show up as NULL, that's the difference between INNER and LEFT — pick LEFT when in doubt during exploration, then tighten to INNER once you know the data is clean.
04

Aggregate functions

Aggregates collapse many rows into one value. Used bare, they summarize the whole table; paired with GROUP BY, they summarize each group separately.

COUNT(*)agg
Counts rows, including NULLs. COUNT(col) skips NULLs in that column.
SELECT COUNT(*) FROM employees;
total row count
SUMagg
Adds a numeric column; ignores NULLs.
SELECT SUM(amount) FROM orders;
AVGagg
Mean of a numeric column; NULLs excluded from both sum and count.
SELECT AVG(salary) FROM employees;
MIN / MAXagg
Smallest/largest value — works on numbers, dates, and strings alike.
SELECT MIN(hire_date), MAX(salary)
FROM employees;
COUNT(DISTINCT)agg
Counts unique non-NULL values.
SELECT COUNT(DISTINCT dept)
FROM employees;
05

GROUP BY & HAVING

GROUP BY buckets rows sharing a value; every non-aggregated column in SELECT must appear in GROUP BY. HAVING filters those buckets — it's WHERE's counterpart, just running one stage later.

SELECT dept, COUNT(*) AS headcount, AVG(salary) AS avg_salary
FROM employees
GROUP BY dept
HAVING COUNT(*) > 3
ORDER BY avg_salary DESC;
reads as: "for each department with more than 3 people, show headcount and average salary, richest department first."
Common mistake: putting COUNT(*) > 3 in WHERE instead of HAVING — WHERE runs before grouping exists, so the aggregate isn't computed yet and the engine rejects it.
06

String functions

Function names vary slightly by engine (MySQL vs Postgres vs SQL Server) — the MySQL spelling is shown, with the notable alternate named where it differs.

CONCATstring
Joins strings together. Postgres/SQL Server also support ||.
SELECT CONCAT(name, ' — ', dept) FROM employees;
Priya — Eng
UPPER / LOWERstring
Case conversion — handy for case-insensitive comparisons.
SELECT UPPER(name) FROM employees WHERE LOWER(dept) = 'sales';
LENGTHstring
Character count of a string. SQL Server uses LEN.
SELECT name FROM employees WHERE LENGTH(name) > 10;
SUBSTRINGstring
Extracts a slice: start position (1-indexed), then length.
SELECT SUBSTRING(name, 1, 3) FROM employees;
'Priyanka' → Pri
TRIM / LTRIM / RTRIMstring
Strips whitespace (or a given character) from ends.
SELECT TRIM('  Priya  ');
'Priya'
REPLACEstring
Swaps every occurrence of a substring for another.
SELECT REPLACE(name, 'a', '@') FROM employees;
LEFT / RIGHTstring
First/last N characters.
SELECT LEFT(name, 1) AS initial FROM employees;
POSITION / INSTRstring
1-based index of a substring; 0 if not found.
SELECT INSTR(name, 'a') FROM employees;
CAST / CONVERTstring
Changes a value's type — string, integer, date, decimal.
SELECT CAST(salary AS CHAR) FROM employees;
07

Numeric functions

Rounding, sign, and arithmetic helpers you'll reach for constantly in reporting queries.

ROUNDmath
Rounds to N decimal places (default 0).
SELECT ROUND(salary / 12, 2) FROM employees;
CEIL / FLOORmath
Round up / down to the nearest integer.
SELECT CEIL(4.2), FLOOR(4.8);
5, 4
ABSmath
Absolute value — drops the sign.
SELECT ABS(salary - 70000) FROM employees;
MOD / %math
Remainder after division — the standard "is it even" check.
SELECT * FROM employees WHERE id % 2 = 0;
POWER / SQRTmath
Exponent and square root.
SELECT POWER(2, 10), SQRT(144);
1024, 12
08

Date & time functions

Dates are the most engine-specific corner of SQL — MySQL syntax shown, Postgres equivalents noted.

NOW / CURDATEdate
Current timestamp / current date. Postgres: CURRENT_TIMESTAMP, CURRENT_DATE.
SELECT * FROM orders WHERE order_date = CURDATE();
DATEDIFFdate
Days between two dates.
SELECT DATEDIFF(NOW(), hire_date) AS days_employed FROM employees;
DATE_ADD / DATE_SUBdate
Shift a date forward/back by an interval.
SELECT DATE_ADD(order_date, INTERVAL 7 DAY) FROM orders;
YEAR / MONTH / DAYdate
Pull a single component out of a date.
SELECT * FROM orders WHERE YEAR(order_date) = 2026;
DATE_FORMATdate
Formats a date as a string. Postgres: TO_CHAR.
SELECT DATE_FORMAT(order_date, '%Y-%m') FROM orders;
'2026-07'
EXTRACTdate
ANSI-standard way to pull a date part; portable across engines.
SELECT EXTRACT(QUARTER FROM order_date) FROM orders;
09

Conditional logic

SQL's if/else — branch on a value or fill in for NULLs inline, without leaving the SELECT list.

CASE WHENlogic
Multi-branch conditional; falls through to ELSE, or NULL if omitted.
SELECT name,
  CASE
    WHEN salary >= 100000 THEN 'senior'
    WHEN salary >= 60000  THEN 'mid'
    ELSE 'junior'
  END AS band
FROM employees;
COALESCElogic
Returns the first non-NULL argument — the standard "default value" pattern.
SELECT COALESCE(manager_id, 0) FROM employees;
NULLIFlogic
Returns NULL if two values are equal — useful for guarding against divide-by-zero.
SELECT amount / NULLIF(quantity, 0) FROM orders;
IFNULL / ISNULLlogic
Two-argument shorthand for the common COALESCE case (MySQL / SQL Server respectively).
SELECT IFNULL(manager_id, -1) FROM employees;
10

Subqueries

A query nested inside another. The key distinction is whether the inner query can run once on its own (independent) or needs a value from the outer row on every iteration (correlated).

KindRunsExample
ScalarOnce, returns one valueSELECT name FROM employees WHERE salary > (SELECT AVG(salary) FROM employees);
INOnce, returns a listSELECT name FROM employees WHERE id IN (SELECT employee_id FROM orders WHERE amount > 500);
EXISTSOnce per outer row, stops at first matchSELECT name FROM employees e WHERE EXISTS (SELECT 1 FROM orders o WHERE o.employee_id = e.id);
CorrelatedOnce per outer row, references the outer tableSELECT name FROM employees e WHERE salary > (SELECT AVG(salary) FROM employees WHERE dept = e.dept);
Performance note: EXISTS short-circuits on the first match and doesn't care about duplicate rows, which usually makes it faster than IN against a large or duplicate-heavy subquery result.
11

Window functions & CTEs

Window functions compute across a set of rows without collapsing them the way GROUP BY does — every input row survives, with a calculated column added alongside it.

WITH ranked AS (
  SELECT name, dept, salary,
         RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS dept_rank
  FROM employees
)
SELECT * FROM ranked WHERE dept_rank <= 2;   -- top 2 earners per dept
ROW_NUMBER()window
Sequential number per row within its partition, always unique.
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC)
RANK() / DENSE_RANK()window
RANK leaves gaps after ties (1,1,3); DENSE_RANK doesn't (1,1,2).
RANK() OVER (ORDER BY salary DESC)
LAG / LEADwindow
Reads a value from the previous/next row — the standard way to compute period-over-period change.
LAG(amount) OVER (ORDER BY order_date)
SUM() OVERwindow
Running total when combined with ORDER BY inside the window.
SUM(amount) OVER (ORDER BY order_date)
WITH (CTE)structure
Names a subquery so it can be referenced like a table — makes multi-step logic readable.
WITH big_orders AS (
  SELECT * FROM orders WHERE amount > 1000
)
SELECT * FROM big_orders;
12

Set operations

Stack two result sets vertically instead of joining them side by side. Both sides need the same number of columns with compatible types.

OperatorBehavior
UNIONCombines rows from both queries, removes duplicates.
UNION ALLCombines rows, keeps duplicates — cheaper since no dedup pass is needed.
INTERSECTOnly rows present in both result sets.
EXCEPT / MINUSRows in the first result set but not the second.
SELECT name FROM employees WHERE dept = 'Sales'
UNION
SELECT name FROM employees WHERE dept = 'Eng';
13

DDL & constraints

Data Definition Language shapes the schema itself — tables, columns, and the rules that guard what can go into them.

CREATE TABLE employees (
  id          INT PRIMARY KEY AUTO_INCREMENT,
  name        VARCHAR(100) NOT NULL,
  dept        VARCHAR(50)  DEFAULT 'Unassigned',
  salary      DECIMAL(10,2) CHECK (salary >= 0),
  manager_id  INT REFERENCES employees(id),
  email       VARCHAR(255) UNIQUE
);

ALTER TABLE employees ADD COLUMN hire_date DATE;
ALTER TABLE employees DROP COLUMN email;
DROP TABLE employees;      -- removes table + data + structure
TRUNCATE TABLE orders;     -- wipes rows, keeps structure, resets auto-increment
ConstraintGuarantees
PRIMARY KEYUniquely identifies each row; implies NOT NULL + UNIQUE.
FOREIGN KEYValue must exist in the referenced table's key column.
UNIQUENo two rows share the same value in this column.
NOT NULLColumn can never be empty.
CHECKValue must satisfy a boolean expression.
DEFAULTValue used automatically when none is supplied on insert.
14

DML & transactions

Data Manipulation Language changes the rows themselves. Wrap multi-statement changes in a transaction so they succeed or fail as one unit.

INSERTdml
Adds new rows.
INSERT INTO employees (name, dept, salary)
VALUES ('Priya', 'Eng', 95000);
UPDATEdml
Modifies existing rows — always pair with WHERE, or every row changes.
UPDATE employees SET salary = salary * 1.1
WHERE dept = 'Eng';
DELETEdml
Removes rows matching a condition; omitting WHERE deletes everything.
DELETE FROM orders WHERE order_date < '2020-01-01';
BEGIN / COMMIT / ROLLBACKtransaction
Groups statements atomically — COMMIT saves them all, ROLLBACK undoes them all.
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
15

Indexes & views

Two ways to make a schema faster to query or easier to work with, without changing the underlying data.

CREATE INDEXperformance
Speeds up lookups/sorts on a column at the cost of slower writes and extra storage.
CREATE INDEX idx_employees_dept ON employees(dept);
CREATE VIEWstructure
Saves a query as a virtual table — same syntax to query it, always reflects live data.
CREATE VIEW high_earners AS
SELECT name, dept, salary FROM employees
WHERE salary > 100000;

SELECT * FROM high_earners;
Rule of thumb: index columns you filter or join on often; don't index columns you rarely query or that change on almost every write — the index maintenance cost outweighs the read benefit.