SQL Syntax Cheat Sheet
A searchable reference for standard SQL syntax. Filter by category and copy the pattern you need.
| Syntax | What it does |
|---|---|
SELECT col1, col2 FROM t | Return the listed columns from a table. |
SELECT * FROM t | Return every column from a table; handy for exploring, but name columns in real queries. |
SELECT DISTINCT col FROM t | Return only the unique values of a column, removing duplicate rows. |
FROM table_name | Name the table (or tables) a query reads rows from. |
SELECT col AS alias | Rename a column in the output using an alias. |
FROM orders AS o | Give a table a short alias to reference elsewhere in the query. |
SELECT price * qty AS total | Select a computed expression and label it with an alias. |
LIMIT 10 | Return at most 10 rows. Postgres and MySQL use LIMIT; SQL Server uses TOP; Oracle uses FETCH FIRST. |
LIMIT 10 OFFSET 20 | Skip 20 rows then return the next 10, the usual pattern for paging. |
OFFSET 20 ROWS | Skip a number of rows before returning results. |
ORDER BY col | Sort the result by a column, ascending by default. |
ORDER BY col DESC | Sort the result by a column in descending order. |
WHERE col = value | Keep only rows where the condition is true. |
WHERE a = 1 AND b = 2 | Combine conditions so every one must be true. |
WHERE a = 1 OR b = 2 | Combine conditions so at least one must be true. |
WHERE NOT active | Negate a condition. |
WHERE col IN (1, 2, 3) | Match any value that appears in the given list. |
WHERE col BETWEEN 1 AND 10 | Match a range; both the low and high bounds are included. |
WHERE name LIKE 'A%' | Pattern match where the percent sign matches any run of zero or more characters. |
WHERE code LIKE 'A_C' | Pattern match where the underscore matches exactly one character. |
WHERE name ILIKE 'a%' | Case-insensitive LIKE in Postgres. MySQL LIKE is already case-insensitive for most collations. |
WHERE col IS NULL | Match rows where the value is NULL, meaning unknown or missing. |
WHERE col IS NOT NULL | Match rows where the value is present. |
FROM a INNER JOIN b ON a.id = b.a_id | Return only rows that have a match in both tables. |
FROM a LEFT JOIN b ON a.id = b.a_id | Return all rows from the left table plus matches from the right; unmatched right columns are NULL. |
FROM a RIGHT JOIN b ON a.id = b.a_id | Return all rows from the right table plus matches from the left; unmatched left columns are NULL. |
FROM a FULL OUTER JOIN b ON a.id = b.a_id | Return all rows from both tables; the non-matching side is NULL. MySQL lacks it and emulates with UNION. |
FROM a CROSS JOIN b | Cartesian product: pair every row of a with every row of b. |
FROM emp e JOIN emp m ON e.mgr_id = m.id | Self join: join a table to itself using two different aliases. |
... JOIN b ON a.id = b.a_id | The ON clause states the condition that pairs rows across tables. |
... JOIN b USING (id) | Shorthand join condition when both tables share the joined column name. |
COUNT(*) | Count rows in the group, including rows that contain NULLs. |
COUNT(col) | Count the non-NULL values in a column. |
COUNT(DISTINCT col) | Count how many distinct non-NULL values a column has. |
SUM(col) | Add up numeric values, ignoring NULLs. |
AVG(col) | Average the numeric values, ignoring NULLs. |
MIN(col) | Return the smallest value in the group. |
MAX(col) | Return the largest value in the group. |
GROUP BY col | Collapse rows into groups so each aggregate is computed per group. |
GROUP BY region, year | Group by more than one column to get a row per combination. |
HAVING COUNT(*) > 1 | Filter groups after aggregation. WHERE filters rows before grouping; HAVING filters after. |
INSERT INTO t (a, b) VALUES (1, 2) | Add one new row with the given column values. |
INSERT INTO t (a, b) VALUES (1, 2), (3, 4) | Insert several rows in a single statement. |
INSERT INTO t (a) SELECT a FROM s | Insert rows produced by a query. |
UPDATE t SET a = 1 | Change a column in every row because no filter is given. Rarely what you want. |
UPDATE t SET a = 1 WHERE id = 5 | Change values only in the rows that match the WHERE clause. |
DELETE FROM t WHERE id = 5 | Remove the rows that match a condition. |
INSERT INTO t (id, a) VALUES (1, 2) ON CONFLICT (id) DO UPDATE SET a = 2 | Upsert in Postgres: insert, or update on a key conflict. MySQL uses ON DUPLICATE KEY UPDATE. |
TRUNCATE TABLE t | Remove all rows fast by deallocating storage; usually cannot be filtered and cannot be easily rolled back in some engines. |
CREATE TABLE t (id INT, name TEXT) | Define a new table with its columns and their types. |
INT, BIGINT, DECIMAL(p,s), VARCHAR(n), TEXT, BOOLEAN, DATE, TIMESTAMP | Common column types. Exact names vary a little by engine, for example SERIAL in Postgres or AUTO_INCREMENT in MySQL. |
id INT PRIMARY KEY | Mark a column as the unique row identifier; it implies both NOT NULL and UNIQUE. |
FOREIGN KEY (a_id) REFERENCES a(id) | Constrain a column so its values must reference a key in another table. |
col INT NOT NULL | Forbid NULLs in a column so a value is always required. |
col TEXT UNIQUE | Require every value in a column to be distinct. |
col INT DEFAULT 0 | Supply a value used when an insert gives none for the column. |
ALTER TABLE t ADD COLUMN c TEXT | Add a new column to an existing table. |
DROP TABLE t | Delete a table together with all its rows and its structure. |
CREATE INDEX idx_t_col ON t (col) | Build an index to speed up lookups and sorts on a column. |
SELECT a FROM x UNION SELECT a FROM y | Combine two result sets and remove duplicate rows. |
SELECT a FROM x UNION ALL SELECT a FROM y | Combine two result sets and keep duplicates, which is faster because no dedupe runs. |
SELECT a FROM x INTERSECT SELECT a FROM y | Return only rows present in both result sets. Older MySQL lacks INTERSECT. |
SELECT a FROM x EXCEPT SELECT a FROM y | Return rows in the first set that are not in the second. Some engines call this MINUS. |
WHERE col IN (SELECT id FROM t) | Subquery in WHERE: filter against the values another query returns. |
WHERE EXISTS (SELECT 1 FROM t WHERE t.a_id = a.id) | True when the correlated subquery returns at least one row; often faster than IN. |
CASE WHEN x > 0 THEN 'pos' ELSE 'neg' END | Inline conditional expression that returns a value per row. |
No syntax matches your search.
Runs entirely in your browser. Nothing you type is sent anywhere; open DevTools and watch the Network tab to verify zero requests.
What this tool does
This is a searchable cheat sheet for standard (ANSI) SQL, the query language shared by PostgreSQL, MySQL, SQLite, SQL Server, and Oracle. Each entry pairs a syntax pattern with a one-sentence, plain-English explanation, grouped into categories: selecting columns, filtering rows, joining tables, aggregating and grouping, modifying data, defining schema, and combining result sets. Where a common engine differs, the explanation notes it briefly. Everything is baked into the page, so it works offline and sends nothing anywhere.
How to use it
Start typing in the Filter box. Entering join narrows the table to
the join family; entering null surfaces IS NULL, IS NOT NULL, and the notes about
NULL handling; entering group shows GROUP BY and HAVING. The
category buttons (All, Query, Filter, Joins, Aggregate, Modify, Schema, Sets)
combine with the text filter, so you can, for example, pick Modify and type
where to find the safe UPDATE and DELETE patterns. Clear the box to see every entry
again. Nothing is submitted and there is no result limit.
Common use cases
- Recalling the exact shape of a join or a GROUP BY while writing a query.
- Checking how NULL, LIKE wildcards, or BETWEEN behave before relying on them.
- Looking up the safe form of UPDATE or DELETE that includes a WHERE clause.
- Comparing UNION vs UNION ALL, or INNER vs LEFT JOIN, side by side.
- Learning SQL or studying for an interview where these patterns come up.
Common pitfalls
- NULL is not equal to NULL. NULL means unknown, so
col = NULLis never true and silently returns nothing. UseIS NULLorIS NOT NULLto test for missing values. - WHERE and HAVING are not interchangeable. WHERE filters rows before grouping and cannot see aggregates; HAVING filters groups after and can. Putting an aggregate in WHERE is an error.
- Forgetting WHERE on UPDATE or DELETE. Without a WHERE clause these statements change or remove every row in the table. Test the condition with a SELECT first, and wrap the change in a transaction when you can.
- Accidental cross joins. Listing two tables without a join condition, or an explicit CROSS JOIN, produces every combination of rows, which can explode the result size. Always state an ON or USING condition unless you truly want the Cartesian product.
Frequently asked questions
- What is the difference between WHERE and HAVING?
- WHERE filters individual rows before they are grouped, and it cannot reference aggregate functions. HAVING filters whole groups after GROUP BY has run, so it can test aggregates such as COUNT(*) or SUM(amount). A rule of thumb: use WHERE to remove rows, and HAVING to remove groups.
- What is the difference between an INNER JOIN and a LEFT JOIN?
- An INNER JOIN returns only the rows that have a match in both tables, so unmatched rows from either side disappear. A LEFT JOIN returns every row from the left table and fills the right-side columns with NULL when there is no match. Use a LEFT JOIN when you want to keep all left rows, for example customers who have placed no orders.
- What does GROUP BY do?
- GROUP BY collapses rows that share the same values in the listed columns into a single group. Aggregate functions such as COUNT, SUM, and AVG are then computed once per group instead of over the whole table. Every column in the SELECT list must either appear in GROUP BY or be wrapped in an aggregate.
- What is the difference between UNION and UNION ALL?
- Both stack the rows of two result sets that have matching columns. UNION removes duplicate rows, which forces the engine to sort or hash the combined output and costs extra time. UNION ALL keeps every row, including duplicates, and is faster, so prefer it when you know the inputs do not overlap or duplicates are fine.
- How is NULL handled in comparisons?
- NULL means unknown, so almost any comparison with it yields unknown rather than true, and unknown rows are dropped by WHERE. That is why NULL = NULL is not true; you must use IS NULL or IS NOT NULL to test for it. Aggregates like SUM and AVG skip NULLs, while COUNT(*) still counts the row.
- What is the difference between DELETE, TRUNCATE, and DROP?
- DELETE removes rows one at a time and can be filtered with a WHERE clause, so it is the only one of the three that removes some rows. TRUNCATE removes every row quickly by deallocating storage and usually cannot be filtered. DROP removes the entire table, including its columns, indexes, and definition, so nothing of the table remains.
- Does this tool send my query anywhere?
- No. This page is a static reference; the full list of SQL patterns is baked into the HTML and all searching and filtering happens in your browser with JavaScript. Nothing you type is uploaded. Open DevTools and watch the Network tab to confirm zero requests while you use it.
Cite this tool
For academic, journalistic, or technical references. Pick a format:
Citations use 2026 as the publication year. Access date is left as a fillable placeholder where the citation style expects one.