SQL Cheat Sheet
Use this SQL cheat sheet as a quick reference for querying, filtering, sorting, joining, grouping, modifying data, creating tables, and writing common database patterns.
Basic SQL Query
Use SELECT to read data from a table.
Learn SQL on Mimo
SQL
SELECT column_name
FROM table_name;
Example:
SQL
SELECT first_name, last_name
FROM customers;
Basic Query Parts
SELECTchooses the columns you want.FROMchooses the table.WHEREfilters rows.ORDER BYsorts results.LIMITcontrols how many rows come back.
Select All Columns
Use * to return every column.
SQL
SELECT *
FROM customers;
Use this for quick checks. In production queries, select only the columns you need.
SQL
SELECT id, email, created_at
FROM customers;
Select Unique Values
Use DISTINCT to remove duplicate values.
SQL
SELECT DISTINCT country
FROM customers;
This returns each country once.
Filter Rows with WHERE
Use WHERE to return rows that match a condition.
SQL
SELECT *
FROM customers
WHERE country = 'Spain';
Common Conditions
Equal to:
SQL
SELECT *
FROM products
WHERE category = 'Books';
Not equal to:
SQL
SELECT *
FROM products
WHERE category <> 'Books';
Greater than:
SQL
SELECT *
FROM orders
WHERE total > 100;
Greater than or equal to:
SQL
SELECT *
FROM orders
WHERE total >= 100;
Less than:
SQL
SELECT *
FROM orders
WHERE total < 100;
Less than or equal to:
SQL
SELECT *
FROM orders
WHERE total <= 100;
Combine Conditions
AND
All conditions must be true.
SQL
SELECT *
FROM customers
WHERE country = 'Germany'
AND status = 'active';
OR
At least one condition must be true.
SQL
SELECT *
FROM customers
WHERE country = 'Germany'
OR country = 'Austria';
NOT
Reverses a condition.
SQL
SELECT *
FROM customers
WHERE NOT status = 'inactive';
Group Conditions with Parentheses
SQL
SELECT *
FROM orders
WHERE status = 'paid'
AND (country = 'Germany' OR country = 'Austria');
Use parentheses when mixing AND and OR to avoid confusing results.
Match Multiple Values with IN
Use IN instead of repeating many OR conditions.
SQL
SELECT *
FROM customers
WHERE country IN ('Germany', 'Austria', 'Switzerland');
Use NOT IN to exclude values.
SQL
SELECT *
FROM customers
WHERE country NOT IN ('Germany', 'Austria');
Match Ranges with BETWEEN
Use BETWEEN for inclusive ranges.
SQL
SELECT *
FROM orders
WHERE total BETWEEN 50 AND 200;
This includes 50 and 200.
Date range example:
SQL
SELECT *
FROM orders
WHERE order_date BETWEEN '2026-01-01' AND '2026-01-31';
Match Text with LIKE
Use LIKE for pattern matching.
Starts With
SQL
SELECT *
FROM customers
WHERE email LIKE 'admin%';
Ends With
SQL
SELECT *
FROM customers
WHERE email LIKE '%@example.com';
Contains
SQL
SELECT *
FROM products
WHERE name LIKE '%phone%';
One Character Match
SQL
SELECT *
FROM products
WHERE code LIKE 'A_1';
Wildcards
%matches any number of characters._matches one character.
Work with NULL
NULL means a missing or unknown value. Do not compare it with =.
Find Missing Values
SQL
SELECT *
FROM customers
WHERE phone IS NULL;
Find Non-Missing Values
SQL
SELECT *
FROM customers
WHERE phone IS NOT NULL;
Replace NULL with a Default
SQL
SELECT
name,
COALESCE(phone, 'No phone') AS phone_display
FROM customers;
COALESCE() returns the first non-null value.
Sort Results with ORDER BY
Sort ascending:
SQL
SELECT *
FROM products
ORDER BY price ASC;
Sort descending:
SQL
SELECT *
FROM products
ORDER BY price DESC;
Sort by multiple columns:
SQL
SELECT *
FROM customers
ORDER BY country ASC, created_at DESC;
If you omit ASC or DESC, most databases sort ascending by default.
Limit Results
Use LIMIT to return fewer rows.
SQL
SELECT *
FROM customers
LIMIT 10;
Use OFFSET for pagination.
SQL
SELECT *
FROM customers
ORDER BY id
LIMIT 10 OFFSET 20;
This skips the first 20 rows and returns the next 10.
Some databases use different syntax. SQL Server often uses TOP:
SQL
SELECT TOP 10 *
FROM customers;
Rename Output with Aliases
Use AS to rename a column in the result.
SQL
SELECT
first_name AS name,
email AS contact_email
FROM customers;
Use aliases for calculated columns:
SQL
SELECT
price,
quantity,
price * quantity AS line_total
FROM order_items;
You can also alias tables:
SQL
SELECT c.email
FROM customers AS c;
Basic Calculations
Add
SQL
SELECT price + tax AS total_price
FROM products;
Subtract
SQL
SELECT original_price - discount AS final_price
FROM products;
Multiply
SQL
SELECT price * quantity AS line_total
FROM order_items;
Divide
SQL
SELECT revenue / orders AS average_order_value
FROM sales_summary;
Be careful when dividing by zero.
SQL
SELECT
revenue / NULLIF(orders, 0) AS average_order_value
FROM sales_summary;
NULLIF(orders, 0) returns NULL when orders is zero, which avoids a division error.
Aggregate Functions
Aggregate functions summarize multiple rows.
Count Rows
SQL
SELECT COUNT(*) AS total_customers
FROM customers;
Count Non-Null Values
SQL
SELECT COUNT(phone) AS customers_with_phone
FROM customers;
Sum
SQL
SELECT SUM(total) AS total_revenue
FROM orders;
Average
SQL
SELECT AVG(total) AS average_order_value
FROM orders;
Minimum
SQL
SELECT MIN(total) AS smallest_order
FROM orders;
Maximum
SQL
SELECT MAX(total) AS largest_order
FROM orders;
Group Results with GROUP BY
Use GROUP BY with aggregate functions.
SQL
SELECT
country,
COUNT(*) AS customer_count
FROM customers
GROUP BY country;
Group by multiple columns:
SQL
SELECT
country,
status,
COUNT(*) AS customer_count
FROM customers
GROUP BY country, status;
Every selected column that is not aggregated should usually appear in the GROUP BY.
Filter Groups with HAVING
Use HAVING to filter grouped results.
SQL
SELECT
customer_id,
SUM(total) AS total_spent
FROM orders
GROUP BY customer_id
HAVING SUM(total) > 500;
WHERE vs HAVING
WHEREfilters rows before grouping.HAVINGfilters groups after aggregation.
Example with both:
SQL
SELECT
customer_id,
SUM(total) AS total_spent
FROM orders
WHERE status = 'paid'
GROUP BY customer_id
HAVING SUM(total) > 500;
Join Tables
Joins combine rows from multiple tables.
Inner Join
Returns rows with matches in both tables.
SQL
SELECT
customers.name,
orders.total
FROM customers
INNER JOIN orders
ON customers.id = orders.customer_id;
Left Join
Returns all rows from the left table and matching rows from the right table.
SQL
SELECT
customers.name,
orders.total
FROM customers
LEFT JOIN orders
ON customers.id = orders.customer_id;
Use LEFT JOIN when you want to keep rows even if the second table has no match.
Right Join
Returns all rows from the right table and matching rows from the left table.
SQL
SELECT
customers.name,
orders.total
FROM customers
RIGHT JOIN orders
ON customers.id = orders.customer_id;
Many developers prefer rewriting this as a LEFT JOIN by switching table order.
Full Outer Join
Returns rows from both tables, even when there is no match.
SQL
SELECT
customers.name,
orders.total
FROM customers
FULL OUTER JOIN orders
ON customers.id = orders.customer_id;
Not every database supports FULL OUTER JOIN.
Join with Table Aliases
Aliases make joins shorter and easier to read.
SQL
SELECT
c.name,
o.total,
o.order_date
FROM customers AS c
INNER JOIN orders AS o
ON c.id = o.customer_id;
Use clear aliases, especially when joining several tables.
Join Multiple Tables
SQL
SELECT
c.name,
o.id AS order_id,
p.name AS product_name
FROM customers AS c
INNER JOIN orders AS o
ON c.id = o.customer_id
INNER JOIN order_items AS oi
ON o.id = oi.order_id
INNER JOIN products AS p
ON oi.product_id = p.id;
This query connects customers, orders, order items, and products.
Find Rows Without a Match
Use LEFT JOIN with IS NULL.
SQL
SELECT c.*
FROM customers AS c
LEFT JOIN orders AS o
ON c.id = o.customer_id
WHERE o.id IS NULL;
This returns customers who have no orders.
Subqueries
A subquery is a query inside another query.
Subquery in WHERE
SQL
SELECT *
FROM products
WHERE price > (
SELECT AVG(price)
FROM products
);
This returns products with prices above the average.
Subquery with IN
SQL
SELECT *
FROM customers
WHERE id IN (
SELECT customer_id
FROM orders
WHERE total > 500
);
This returns customers who placed large orders.
Subquery in FROM
SQL
SELECT
country,
AVG(customer_count) AS average_count
FROM (
SELECT
country,
COUNT(*) AS customer_count
FROM customers
GROUP BY country
) AS country_counts
GROUP BY country;
Subqueries can make complex logic easier to split into steps.
Common Table Expressions
Use WITH to create a named temporary result inside a query.
SQL
WITH customer_totals AS (
SELECT
customer_id,
SUM(total) AS total_spent
FROM orders
GROUP BY customer_id
)
SELECT *
FROM customer_totals
WHERE total_spent > 500;
CTEs often read better than deeply nested subqueries.
Insert Data
Insert One Row
SQL
INSERT INTO customers (name, email, country)
VALUES ('Alex Smith', 'alex@example.com', 'Germany');
Insert Multiple Rows
SQL
INSERT INTO customers (name, email, country)
VALUES
('Alex Smith', 'alex@example.com', 'Germany'),
('Sam Lee', 'sam@example.com', 'Austria'),
('Taylor Kim', 'taylor@example.com', 'Switzerland');
Always list the column names. This makes inserts safer if the table changes later.
Update Data
Use UPDATE to change existing rows.
SQL
UPDATE customers
SET status = 'active'
WHERE id = 1;
Update multiple columns:
SQL
UPDATE customers
SET
status = 'active',
updated_at = CURRENT_TIMESTAMP
WHERE id = 1;
Always include a WHERE clause unless you truly want to update every row.
Delete Data
Use DELETE to remove rows.
SQL
DELETE FROM customers
WHERE id = 1;
Delete rows that match a condition:
SQL
DELETE FROM orders
WHERE status = 'cancelled';
Always check the rows first:
SQL
SELECT *
FROM orders
WHERE status = 'cancelled';
Then run the delete only after confirming the result.
Create a Table
Use CREATE TABLE to define a new table.
SQL
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE,
country VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Common Column Types
INTEGERstores whole numbers.DECIMALstores exact decimal values, often for money.VARCHAR(n)stores text with a maximum length.TEXTstores longer text.DATEstores a date.TIMESTAMPstores a date and time.BOOLEANstores true or false values.
Exact type names vary between databases.
Constraints
Constraints protect data quality.
Primary Key
SQL
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name VARCHAR(100)
);
A primary key uniquely identifies each row.
Not Null
SQL
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
email VARCHAR(255) NOT NULL
);
NOT NULL requires a value.
Unique
SQL
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
email VARCHAR(255) UNIQUE
);
UNIQUE prevents duplicate values.
Default
SQL
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
status VARCHAR(20) DEFAULT 'active'
);
DEFAULT sets a value when none is provided.
Foreign Key
SQL
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER,
total DECIMAL(10, 2),
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
A foreign key links one table to another.
Alter a Table
Use ALTER TABLE to change an existing table.
Add a Column
SQL
ALTER TABLE customers
ADD phone VARCHAR(50);
Rename a Column
SQL
ALTER TABLE customers
RENAME COLUMN phone TO phone_number;
Drop a Column
SQL
ALTER TABLE customers
DROP COLUMN phone_number;
Support for ALTER TABLE differs between databases.
Drop a Table
Use DROP TABLE to remove a table.
SQL
DROP TABLE customers;
Use this carefully. It removes the table structure and its data.
Safer version:
SQL
DROP TABLE IF EXISTS customers;
Create an Index
Indexes can make queries faster.
SQL
CREATE INDEX idx_customers_email
ON customers(email);
Index multiple columns:
SQL
CREATE INDEX idx_orders_customer_date
ON orders(customer_id, order_date);
Use indexes on columns often used in:
WHEREJOINORDER BYGROUP BY
Do not add indexes everywhere. They can slow down inserts and updates.
Views
A view saves a query as a reusable virtual table.
SQL
CREATE VIEW active_customers AS
SELECT
id,
name,
email
FROM customers
WHERE status = 'active';
Query the view:
SQL
SELECT *
FROM active_customers;
Use views for repeated reporting logic or simplified access to complex joins.
Transactions
Transactions group changes together.
SQL
BEGIN;
UPDATE accounts
SET balance = balance - 100
WHERE id = 1;
UPDATE accounts
SET balance = balance + 100
WHERE id = 2;
COMMIT;
If something goes wrong, roll back:
SQL
ROLLBACK;
Use transactions when multiple changes must succeed or fail together.
Case Expressions
Use CASE to create conditional values.
SQL
SELECT
name,
total,
CASE
WHEN total >= 500 THEN 'High value'
WHEN total >= 100 THEN 'Medium value'
ELSE 'Low value'
END AS order_value_group
FROM orders;
CASE works well for labels, categories, and conditional calculations.
String Functions
Function names vary slightly across databases, but these are common patterns.
Convert to Lowercase
SQL
SELECT LOWER(email) AS normalized_email
FROM customers;
Convert to Uppercase
SQL
SELECT UPPER(country) AS country_code
FROM customers;
Get String Length
SQL
SELECT LENGTH(name) AS name_length
FROM customers;
Some databases use LEN() instead of LENGTH().
Join Strings
SQL
SELECT CONCAT(first_name, ' ', last_name) AS full_name
FROM customers;
Some databases also support ||:
SQL
SELECT first_name || ' ' || last_name AS full_name
FROM customers;
Number Functions
Round
SQL
SELECT ROUND(total, 2) AS rounded_total
FROM orders;
Absolute Value
SQL
SELECT ABS(balance) AS positive_balance
FROM accounts;
Ceiling
SQL
SELECT CEIL(price) AS rounded_up
FROM products;
Some databases use CEILING() instead of CEIL().
Floor
SQL
SELECT FLOOR(price) AS rounded_down
FROM products;
Date and Time
Date functions vary between databases, but the core ideas are similar.
Current Date
SQL
SELECT CURRENT_DATE;
Current Timestamp
SQL
SELECT CURRENT_TIMESTAMP;
Filter by Date
SQL
SELECT *
FROM orders
WHERE order_date >= '2026-01-01';
Sort by Date
SQL
SELECT *
FROM orders
ORDER BY order_date DESC;
Group by Date
SQL
SELECT
order_date,
COUNT(*) AS order_count
FROM orders
GROUP BY order_date;
For production reporting, you may need database-specific functions to extract day, month, or year.
Window Functions
Window functions calculate values across related rows without collapsing results.
Row Number
SQL
SELECT
customer_id,
order_date,
total,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY order_date DESC
) AS order_rank
FROM orders;
This ranks each customer’s orders from newest to oldest.
Running Total
SQL
SELECT
order_date,
total,
SUM(total) OVER (
ORDER BY order_date
) AS running_total
FROM orders;
Rank Rows
SQL
SELECT
customer_id,
total,
RANK() OVER (
ORDER BY total DESC
) AS sales_rank
FROM orders;
Use window functions for rankings, running totals, moving averages, and grouped comparisons.
Set Operations
Set operations combine results from multiple queries.
UNION
Combines results and removes duplicates.
SQL
SELECT email
FROM customers
UNION
SELECT email
FROM newsletter_signups;
UNION ALL
Combines results and keeps duplicates.
SQL
SELECT email
FROM customers
UNION ALL
SELECT email
FROM newsletter_signups;
INTERSECT
Returns values found in both queries.
SQL
SELECT email
FROM customers
INTERSECT
SELECT email
FROM newsletter_signups;
EXCEPT
Returns values from the first query that are not in the second.
SQL
SELECT email
FROM customers
EXCEPT
SELECT email
FROM unsubscribed_users;
Database support for INTERSECT and EXCEPT can vary.
Common SQL Query Order
SQL is written in one order but processed in another.
Writing Order
SQL
SELECT
country,
COUNT(*) AS customer_count
FROM customers
WHERE status = 'active'
GROUP BY country
HAVING COUNT(*) > 10
ORDER BY customer_count DESC
LIMIT 5;
Logical Processing Order
FROMWHEREGROUP BYHAVINGSELECTORDER BYLIMIT
Knowing this helps explain why aliases may not work in some parts of a query.
Common SQL Patterns
Find Duplicate Emails
SQL
SELECT
email,
COUNT(*) AS email_count
FROM customers
GROUP BY email
HAVING COUNT(*) > 1;
Find Latest Order per Customer
SQL
WITH ranked_orders AS (
SELECT
customer_id,
id AS order_id,
order_date,
total,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY order_date DESC
) AS row_num
FROM orders
)
SELECT *
FROM ranked_orders
WHERE row_num = 1;
Count Orders by Status
SQL
SELECT
status,
COUNT(*) AS order_count
FROM orders
GROUP BY status;
Calculate Revenue by Month
SQL
SELECT
DATE_TRUNC('month', order_date) AS month,
SUM(total) AS revenue
FROM orders
GROUP BY DATE_TRUNC('month', order_date)
ORDER BY month;
DATE_TRUNC() works in PostgreSQL. Other databases use different date functions.
Find Customers with No Orders
SQL
SELECT c.*
FROM customers AS c
LEFT JOIN orders AS o
ON c.id = o.customer_id
WHERE o.id IS NULL;
Find Top Products by Revenue
SQL
SELECT
p.name,
SUM(oi.quantity * oi.price) AS revenue
FROM products AS p
INNER JOIN order_items AS oi
ON p.id = oi.product_id
GROUP BY p.name
ORDER BY revenue DESC
LIMIT 10;
Common Mistakes
Forgetting the WHERE Clause in UPDATE
SQL
UPDATE customers
SET status = 'inactive';
This updates every row.
Better:
SQL
UPDATE customers
SET status = 'inactive'
WHERE id = 42;
Forgetting the WHERE Clause in DELETE
SQL
DELETE FROM customers;
This deletes every row.
Better:
SQL
DELETE FROM customers
WHERE id = 42;
Using = with NULL
SQL
SELECT *
FROM customers
WHERE phone = NULL;
Better:
SQL
SELECT *
FROM customers
WHERE phone IS NULL;
Use IS NULL and IS NOT NULL.
Filtering Aggregates with WHERE
SQL
SELECT
customer_id,
SUM(total) AS total_spent
FROM orders
WHERE SUM(total) > 500
GROUP BY customer_id;
Better:
SQL
SELECT
customer_id,
SUM(total) AS total_spent
FROM orders
GROUP BY customer_id
HAVING SUM(total) > 500;
Use HAVING for aggregate filters.
Selecting Columns Not in GROUP BY
SQL
SELECT
country,
email,
COUNT(*) AS customer_count
FROM customers
GROUP BY country;
Better:
SQL
SELECT
country,
COUNT(*) AS customer_count
FROM customers
GROUP BY country;
Grouped queries should select grouped columns or aggregate values.
Creating Duplicate Rows with Joins
SQL
SELECT
c.name,
o.total
FROM customers AS c
INNER JOIN orders AS o
ON c.id = o.customer_id;
This is correct if you expect one row per order. It may look wrong if you expected one row per customer.
To summarize per customer:
SQL
SELECT
c.name,
SUM(o.total) AS total_spent
FROM customers AS c
INNER JOIN orders AS o
ON c.id = o.customer_id
GROUP BY c.name;
Debugging Tips
Start with a Small Query
SQL
SELECT *
FROM orders
LIMIT 10;
Check the data before writing complex filters or joins.
Check Join Keys
SQL
SELECT
c.id AS customer_id,
o.customer_id AS order_customer_id
FROM customers AS c
INNER JOIN orders AS o
ON c.id = o.customer_id
LIMIT 10;
Make sure the columns actually match.
Count Rows Before and After a Join
SQL
SELECT COUNT(*)
FROM customers;
SQL
SELECT COUNT(*)
FROM customers AS c
INNER JOIN orders AS o
ON c.id = o.customer_id;
A large row increase may mean one-to-many relationships are creating duplicates.
Test Filters One at a Time
Start simple:
SQL
SELECT *
FROM orders
WHERE status = 'paid';
Then add another condition:
SQL
SELECT *
FROM orders
WHERE status = 'paid'
AND total > 100;
Use Aliases for Readability
SQL
SELECT
c.name,
o.total
FROM customers AS c
INNER JOIN orders AS o
ON c.id = o.customer_id;
Aliases make long queries easier to scan.
Format Queries Clearly
SQL
SELECT
customer_id,
SUM(total) AS total_spent
FROM orders
WHERE status = 'paid'
GROUP BY customer_id
ORDER BY total_spent DESC;
Line breaks make SQL easier to edit and review.
Quick Reference
Select Data
SQL
SELECT column_name
FROM table_name;
Select All Columns
SQL
SELECT *
FROM table_name;
Filter Rows
SQL
SELECT *
FROM table_name
WHERE column_name = 'value';
Sort Results
SQL
SELECT *
FROM table_name
ORDER BY column_name DESC;
Limit Results
SQL
SELECT *
FROM table_name
LIMIT 10;
Count Rows
SQL
SELECT COUNT(*)
FROM table_name;
Group Results
SQL
SELECT
column_name,
COUNT(*) AS row_count
FROM table_name
GROUP BY column_name;
Filter Groups
SQL
SELECT
column_name,
COUNT(*) AS row_count
FROM table_name
GROUP BY column_name
HAVING COUNT(*) > 5;
Join Tables
SQL
SELECT
a.column_name,
b.column_name
FROM table_a AS a
INNER JOIN table_b AS b
ON a.id = b.table_a_id;
Insert Data
SQL
INSERT INTO table_name (column_one, column_two)
VALUES ('value one', 'value two');
Update Data
SQL
UPDATE table_name
SET column_name = 'new value'
WHERE id = 1;
Delete Data
SQL
DELETE FROM table_name
WHERE id = 1;
Create a Table
SQL
CREATE TABLE table_name (
id INTEGER PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
Create an Index
SQL
CREATE INDEX index_name
ON table_name(column_name);
Use a CTE
SQL
WITH result_name AS (
SELECT *
FROM table_name
)
SELECT *
FROM result_name;
Join 35M+ people learning for free on Mimo
4.8 out of 5 across 1M+ reviews
Check us out on Apple AppStore, Google Play Store, and Trustpilot