SQL Cheat Sheet: Essential Queries and Commands
A practical SQL cheat sheet covering SELECT, WHERE, JOINs, aggregates, GROUP BY, INSERT, UPDATE, DELETE, and table basics — with copy-ready code examples.
A quick reference for the SQL you reach for every day. Examples use standard ANSI SQL and run on PostgreSQL, MySQL, SQL Server, SQLite, and Oracle unless a dialect note says otherwise.
SELECT Basics
SELECT reads rows from a table. Name the columns you want, or use * to grab them all.
-- Specific columns
SELECT first_name, last_name FROM users;
-- Every column (handy for exploring, avoid in production code)
SELECT * FROM users;
Rename a column in the output with AS. Use DISTINCT to collapse duplicate rows down to unique values.
-- Column aliases (AS is optional but clearer)
SELECT first_name AS name, email AS contact FROM users;
-- Unique values only
SELECT DISTINCT country FROM users;
Filtering with WHERE
WHERE keeps only the rows that match a condition. Combine conditions with AND, OR, and NOT.
| Operator | Meaning |
|---|---|
= | Equal to |
<> or != | Not equal to |
< > | Less than / greater than |
<= >= | Less than or equal / greater than or equal |
SELECT * FROM products WHERE price > 100;
SELECT * FROM products WHERE category = 'books' AND price < 20;
SELECT * FROM users WHERE NOT country = 'US';
Match a set of values with IN, a range with BETWEEN, and a text pattern with LIKE.
-- Any value in a list
SELECT * FROM users WHERE country IN ('US', 'CA', 'MX');
-- Inclusive range (both ends count)
SELECT * FROM products WHERE price BETWEEN 10 AND 50;
-- Pattern matching: % = any number of chars, _ = exactly one char
SELECT * FROM users WHERE email LIKE '%@gmail.com';
SELECT * FROM users WHERE phone LIKE '555-___-____';
NULL means “unknown,” so it never equals anything — not even another NULL. Test for it with IS NULL / IS NOT NULL, never = NULL.
SELECT * FROM users WHERE phone IS NULL;
SELECT * FROM users WHERE phone IS NOT NULL;
Sorting and Limiting
ORDER BY sorts the result. Default is ascending (ASC); use DESC for largest/newest first. LIMIT caps the number of rows returned.
SELECT * FROM products ORDER BY price DESC;
SELECT * FROM products ORDER BY category ASC, price DESC;
-- First 10 rows (PostgreSQL, MySQL, SQLite)
SELECT * FROM products ORDER BY price DESC LIMIT 10;
Row-limiting syntax differs by database:
| Database | Syntax |
|---|---|
| PostgreSQL / MySQL / SQLite | LIMIT 10 |
| SQL Server | SELECT TOP 10 ... |
| Oracle | FETCH FIRST 10 ROWS ONLY |
-- SQL Server
SELECT TOP 10 * FROM products ORDER BY price DESC;
-- Oracle (also valid on modern PostgreSQL)
SELECT * FROM products ORDER BY price DESC FETCH FIRST 10 ROWS ONLY;
Aggregate Functions
Aggregates collapse many rows into a single value.
| Function | Returns |
|---|---|
COUNT() | Number of rows |
SUM() | Total of a numeric column |
AVG() | Average value |
MIN() | Smallest value |
MAX() | Largest value |
SELECT COUNT(*) FROM orders;
SELECT SUM(amount) FROM orders;
SELECT AVG(price), MIN(price), MAX(price) FROM products;
COUNT(*) counts every row; COUNT(column) counts only rows where that column is not NULL.
Grouping with GROUP BY and HAVING
GROUP BY splits rows into buckets so aggregates run per group. HAVING then filters those groups.
-- One row per country, with its order count
SELECT country, COUNT(*) AS order_count
FROM orders
GROUP BY country;
-- Only keep countries with more than 100 orders
SELECT country, COUNT(*) AS order_count
FROM orders
GROUP BY country
HAVING COUNT(*) > 100;
The difference: WHERE filters individual rows before grouping, while HAVING filters groups after aggregation. You cannot use an aggregate like COUNT() in WHERE.
-- WHERE filters rows first, HAVING filters the grouped result
SELECT country, AVG(amount) AS avg_amount
FROM orders
WHERE status = 'paid'
GROUP BY country
HAVING AVG(amount) > 50;
Joins
Joins combine rows from two tables based on a matching condition in the ON clause.
| Join type | Returns |
|---|---|
INNER JOIN | Only rows with a match in both tables |
LEFT JOIN | All left rows, plus matches (NULLs where none) |
RIGHT JOIN | All right rows, plus matches (NULLs where none) |
FULL OUTER JOIN | All rows from both, matched where possible |
-- Customers and their orders (only customers who ordered)
SELECT users.name, orders.amount
FROM users
INNER JOIN orders ON orders.user_id = users.id;
-- All users, even those with no orders (amount is NULL for them)
SELECT users.name, orders.amount
FROM users
LEFT JOIN orders ON orders.user_id = users.id;
Note: MySQL does not support FULL OUTER JOIN directly — emulate it by UNION-ing a LEFT JOIN and a RIGHT JOIN.
Modifying Data
Add new rows with INSERT INTO. List the columns, then the matching values.
INSERT INTO users (first_name, last_name, email)
VALUES ('Ada', 'Lovelace', 'ada@example.com');
-- Insert several rows at once
INSERT INTO users (first_name, email) VALUES
('Grace', 'grace@example.com'),
('Alan', 'alan@example.com');
UPDATE changes existing rows; DELETE FROM removes them. Always include a WHERE clause — without one, you change or delete every row in the table.
-- Update one user
UPDATE users SET email = 'ada.l@example.com' WHERE id = 1;
-- Delete one user
DELETE FROM users WHERE id = 42;
-- DANGER: no WHERE — this wipes or overwrites the ENTIRE table
UPDATE users SET active = false;
DELETE FROM users;
Table Basics
CREATE TABLE defines a new table, its columns, and their data types. A PRIMARY KEY uniquely identifies each row.
| Data type | Stores |
|---|---|
INT | Whole numbers |
VARCHAR(n) | Variable-length text up to n chars |
DATE | Calendar dates |
BOOLEAN / BOOL | True / false |
CREATE TABLE users (
id INT PRIMARY KEY,
first_name VARCHAR(50),
last_name VARCHAR(50),
email VARCHAR(255),
signup_date DATE,
active BOOLEAN
);
Change a table’s structure with ALTER TABLE. Remove a table entirely with DROP TABLE (this deletes the data too).
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
ALTER TABLE users DROP COLUMN phone;
DROP TABLE users;
Good-Practice Tips
- Test your filter with
SELECTfirst. RunSELECT * FROM t WHERE ...to confirm exactly which rows match before turning that sameWHEREinto anUPDATEorDELETE. - List columns explicitly.
SELECT id, nameandINSERT INTO t (a, b)keep your queries stable when someone adds or reorders columns later. ReserveSELECT *for quick exploration. - Mind NULLs. Comparisons against NULL yield “unknown,” not true, so they silently drop rows. Use
IS NULL, and rememberCOUNT(column)skips NULLs whileCOUNT(*)does not. - Wrap multi-statement changes in a transaction (
BEGIN; ... COMMIT;) so you canROLLBACKif something looks wrong.
Want more references like this? Browse our other developer guides for cheat sheets and tutorials.