SQL Cheat Sheet for Developers
Level: beginner · ~20 min read · Intent: informational
Audience: backend developers, data analysts, data engineers, technical teams, students, software engineers
Prerequisites
- basic familiarity with databases
- basic understanding of tables, rows, and columns
Key takeaways
- A strong SQL cheat sheet should cover not just syntax, but also the query patterns developers use constantly, including joins, grouping, filtering, updates, inserts, and conditional logic.
- The most useful SQL habit is understanding the flow of a query clearly: choose rows, filter rows, join related data, group or aggregate when needed, and only then sort or limit the result.
FAQ
- What should a SQL cheat sheet include?
- A good SQL cheat sheet should include core query syntax, filtering, joins, grouping, aggregates, CASE expressions, subqueries, CTEs, inserts, updates, deletes, and practical query patterns developers use regularly.
- Is SQL syntax the same in every database?
- The core SQL syntax is very similar across major databases, but some features, functions, date handling, limit syntax, upsert patterns, and advanced features differ between PostgreSQL, MySQL, SQL Server, SQLite, and others.
- What are the most important SQL commands for developers?
- The most important SQL commands for developers are SELECT, INSERT, UPDATE, DELETE, JOIN, GROUP BY, ORDER BY, CASE, COUNT, SUM, AVG, and common patterns like CTEs and subqueries.
- What is the best way to learn SQL quickly?
- The fastest way to learn SQL is to combine a cheat sheet with real table data and practical projects, then repeatedly write queries that use filtering, joins, grouping, and reporting logic.
SQL is one of the most useful tools a developer can know because it sits right at the boundary between application logic and data.
Whether you are building:
- a backend API
- a SaaS platform
- a reporting dashboard
- a BI workflow
- an analytics pipeline
- or an internal business tool
you will almost always run into SQL.
The problem is not that SQL is hard to start.
The problem is that once you move past basic SELECT statements, there are a lot of patterns to remember:
- filtering
- joins
- grouping
- aggregates
- conditional logic
- updates
- deletes
- subqueries
- CTEs
- and database-specific differences
That is why a practical SQL cheat sheet is useful.
This article is designed to be a high-value reference you can actually use while working. It covers the syntax and patterns developers use most often, with examples that are simple enough to scan quickly but realistic enough to matter.
How to use this SQL cheat sheet
This cheat sheet is organized around everyday developer tasks.
That means it is less about theoretical SQL categories and more about questions like:
- how do I select only the columns I need?
- how do I join two tables?
- how do I count rows by category?
- how do I update records safely?
- how do I write a CTE?
- how do I use CASE?
- how do I remove duplicates?
- how do I inspect query performance?
The examples are written in generally portable SQL where possible. But remember:
Not all SQL dialects are identical.
The core ideas are consistent across:
- PostgreSQL
- MySQL
- SQL Server
- SQLite
- MariaDB
- Oracle
But some syntax differs, especially for:
- limit and pagination
- date functions
- string functions
- upsert behavior
- JSON support
- and advanced analytical features
So treat this as a practical cross-database developer guide, then adjust for your engine where needed.
The most important SQL rule for developers
Before the syntax, remember this:
A good SQL query is not just valid. It is clear, targeted, and shaped around the data you actually need.
That means good SQL usually does this:
- selects only the needed columns
- filters early
- joins only what is necessary
- groups only when useful
- and avoids doing more work than required
That one mindset improves both:
- correctness
- and performance
A lot of bad SQL is not bad because it is illegal. It is bad because it is vague, wasteful, or hard to reason about later.
Basic SELECT syntax
The most fundamental query is SELECT.
Select all columns
SELECT *
FROM users;
Use this carefully. It is convenient, but in real applications it often returns more data than needed.
Select specific columns
SELECT id, email, created_at
FROM users;
This is usually better than SELECT * because it:
- reduces data transfer
- makes the query intent clearer
- and can improve performance in real systems
Rename columns with aliases
SELECT
id,
email AS user_email,
created_at AS signup_date
FROM users;
Aliases are useful when:
- you want clearer output
- you need readable report columns
- or the original column names are not ideal in the result set
Filtering with WHERE
WHERE filters rows before they are returned.
Basic equality
SELECT *
FROM users
WHERE country = 'South Africa';
Greater than / less than
SELECT *
FROM orders
WHERE total_amount > 100;
Combine conditions with AND
SELECT *
FROM users
WHERE country = 'South Africa'
AND is_active = true;
Combine conditions with OR
SELECT *
FROM users
WHERE country = 'South Africa'
OR country = 'Namibia';
Use parentheses for clarity
SELECT *
FROM users
WHERE is_active = true
AND (country = 'South Africa' OR country = 'Namibia');
Always use parentheses when a mixed AND / OR condition could be misread.
Useful WHERE patterns
IN
SELECT *
FROM users
WHERE country IN ('South Africa', 'Namibia', 'Botswana');
BETWEEN
SELECT *
FROM orders
WHERE total_amount BETWEEN 100 AND 500;
LIKE
SELECT *
FROM users
WHERE email LIKE '%@gmail.com';
Common patterns:
%text%containstext%starts with%textends with
IS NULL
SELECT *
FROM users
WHERE phone_number IS NULL;
IS NOT NULL
SELECT *
FROM users
WHERE phone_number IS NOT NULL;
Important:
never use = NULL.
Use IS NULL.
Sorting with ORDER BY
Ascending sort
SELECT *
FROM products
ORDER BY price ASC;
Descending sort
SELECT *
FROM products
ORDER BY price DESC;
Sort by multiple columns
SELECT *
FROM orders
ORDER BY status ASC, created_at DESC;
This means:
- sort by status first
- then by newest within each status group
Limiting results
Different databases vary slightly here, but the common idea is to limit how many rows you return.
LIMIT
SELECT *
FROM posts
ORDER BY created_at DESC
LIMIT 10;
This is common in:
- PostgreSQL
- MySQL
- SQLite
OFFSET
SELECT *
FROM posts
ORDER BY created_at DESC
LIMIT 10 OFFSET 20;
This gives:
- 10 rows
- after skipping the first 20
Useful for pagination, but large offsets can become expensive on big tables.
DISTINCT
Use DISTINCT when you want unique values.
Distinct one column
SELECT DISTINCT country
FROM users;
Distinct combinations
SELECT DISTINCT country, city
FROM users;
This returns unique country-city combinations.
SQL comments
Single-line comment
-- This query gets active users
SELECT *
FROM users
WHERE is_active = true;
Multi-line comment
/*
This query summarizes
monthly revenue
*/
SELECT *
FROM orders;
Comments matter because SQL often becomes much easier to maintain when business logic is explained.
JOINs
JOINs combine data from related tables.
They are one of the most important SQL skills.
Suppose you have:
customersorders
and orders.customer_id links to customers.customer_id.
INNER JOIN
Returns only rows that match in both tables.
SELECT
o.order_id,
c.customer_name,
o.total_amount
FROM orders o
INNER JOIN customers c
ON o.customer_id = c.customer_id;
This is the most common join type.
LEFT JOIN
Returns all rows from the left table, even if there is no match on the right.
SELECT
c.customer_name,
o.order_id
FROM customers c
LEFT JOIN orders o
ON c.customer_id = o.customer_id;
This is useful when you want:
- all customers
- including those with no orders
RIGHT JOIN
Less common in many codebases, but conceptually the reverse of LEFT JOIN.
SELECT
c.customer_name,
o.order_id
FROM customers c
RIGHT JOIN orders o
ON c.customer_id = o.customer_id;
Many developers prefer rewriting RIGHT JOIN as LEFT JOIN for readability.
FULL OUTER JOIN
Returns matching rows plus unmatched rows from both sides.
SELECT
c.customer_name,
o.order_id
FROM customers c
FULL OUTER JOIN orders o
ON c.customer_id = o.customer_id;
Supported in some databases, not all.
Common join pattern
A very common application query looks like this:
SELECT
o.order_id,
o.created_at,
c.customer_name,
c.email
FROM orders o
JOIN customers c
ON o.customer_id = c.customer_id
WHERE o.status = 'Paid'
ORDER BY o.created_at DESC;
That is the kind of query developers write constantly.
Aggregate functions
Aggregate functions summarize data.
The most common ones are:
COUNTSUMAVGMINMAX
COUNT all rows
SELECT COUNT(*)
FROM orders;
COUNT non-null values
SELECT COUNT(email)
FROM users;
SUM
SELECT SUM(total_amount)
FROM orders;
AVG
SELECT AVG(total_amount)
FROM orders;
MIN and MAX
SELECT MIN(price), MAX(price)
FROM products;
GROUP BY
Use GROUP BY when you want aggregates per category or per entity.
Count orders by customer
SELECT
customer_id,
COUNT(*) AS order_count
FROM orders
GROUP BY customer_id;
Revenue by month
SELECT
order_month,
SUM(total_amount) AS total_revenue
FROM monthly_orders
GROUP BY order_month;
Average salary by department
SELECT
department_id,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id;
HAVING
Use HAVING to filter grouped results after aggregation.
Customers with more than five orders
SELECT
customer_id,
COUNT(*) AS order_count
FROM orders
GROUP BY customer_id
HAVING COUNT(*) > 5;
Remember:
WHEREfilters rows before groupingHAVINGfilters groups after aggregation
CASE WHEN
CASE adds conditional logic.
Basic categorization
SELECT
customer_id,
total_spend,
CASE
WHEN total_spend >= 5000 THEN 'VIP'
WHEN total_spend >= 1000 THEN 'Regular'
ELSE 'Basic'
END AS customer_segment
FROM customers;
Conditional aggregation
SELECT
SUM(CASE WHEN status = 'Paid' THEN 1 ELSE 0 END) AS paid_orders,
SUM(CASE WHEN status = 'Refunded' THEN 1 ELSE 0 END) AS refunded_orders
FROM orders;
This is one of the most useful SQL reporting patterns.
Subqueries
A subquery is a query inside another query.
Simple subquery in WHERE
SELECT *
FROM customers
WHERE customer_id IN (
SELECT customer_id
FROM orders
WHERE total_amount > 1000
);
This returns customers who placed high-value orders.
Scalar subquery
SELECT *
FROM products
WHERE price > (
SELECT AVG(price)
FROM products
);
This returns products priced above the average.
Common Table Expressions (CTEs)
CTEs use WITH and are very useful for readability.
Basic CTE
WITH high_value_orders AS (
SELECT *
FROM orders
WHERE total_amount > 1000
)
SELECT *
FROM high_value_orders;
CTE with aggregation
WITH customer_revenue AS (
SELECT
customer_id,
SUM(total_amount) AS total_revenue
FROM orders
GROUP BY customer_id
)
SELECT *
FROM customer_revenue
WHERE total_revenue > 5000;
CTEs are especially useful when a query would otherwise become hard to read.
INSERT
Insert one row
INSERT INTO users (email, country, is_active)
VALUES ('alice@example.com', 'South Africa', true);
Insert multiple rows
INSERT INTO users (email, country, is_active)
VALUES
('alice@example.com', 'South Africa', true),
('bob@example.com', 'Namibia', true),
('cara@example.com', 'Botswana', false);
UPDATE
Update one or more rows
UPDATE users
SET is_active = false
WHERE last_login_at < '2025-01-01';
Always be careful with UPDATE.
Without a WHERE clause, it updates every row.
Update multiple columns
UPDATE products
SET price = 49.99,
updated_at = CURRENT_TIMESTAMP
WHERE product_id = 10;
DELETE
Delete specific rows
DELETE FROM users
WHERE is_active = false;
Again, without WHERE, every row is deleted.
That is why delete safety matters.
TRUNCATE
TRUNCATE removes all rows from a table quickly.
TRUNCATE TABLE temp_import_data;
Use this carefully. It is not the same as a targeted delete.
CREATE TABLE
Basic table definition
CREATE TABLE customers (
customer_id INT PRIMARY KEY,
customer_name VARCHAR(255) NOT NULL,
email VARCHAR(255),
created_at TIMESTAMP
);
This defines:
- a primary key
- a required column with
NOT NULL - normal text fields
- a timestamp column
ALTER TABLE
Add a column
ALTER TABLE customers
ADD phone_number VARCHAR(50);
Rename a column
ALTER TABLE customers
RENAME COLUMN phone_number TO mobile_number;
Add a constraint
ALTER TABLE customers
ADD CONSTRAINT uq_customers_email UNIQUE (email);
DROP TABLE
DROP TABLE customers;
Use carefully. This removes the table definition itself, not just the rows.
Primary keys and unique constraints
Primary key
A primary key uniquely identifies each row.
CREATE TABLE products (
product_id INT PRIMARY KEY,
product_name VARCHAR(255) NOT NULL
);
Unique constraint
Prevents duplicate values in a column or column combination.
CREATE TABLE users (
user_id INT PRIMARY KEY,
email VARCHAR(255) UNIQUE
);
This is important for data integrity.
Basic index syntax
Indexes help queries find rows more efficiently.
Create an index
CREATE INDEX idx_orders_customer_id
ON orders (customer_id);
Composite index
CREATE INDEX idx_orders_customer_status_created
ON orders (customer_id, status, created_at);
Composite indexes are often very important for real application queries.
Finding duplicates
Find duplicate emails
SELECT
email,
COUNT(*) AS duplicate_count
FROM users
GROUP BY email
HAVING COUNT(*) > 1;
Preview duplicate rows with ROW_NUMBER
WITH ranked_rows AS (
SELECT
id,
email,
ROW_NUMBER() OVER (
PARTITION BY email
ORDER BY created_at ASC
) AS rn
FROM users
)
SELECT *
FROM ranked_rows
WHERE rn > 1;
This is one of the most useful data-cleanup patterns in SQL.
Window functions
Window functions are more advanced, but extremely useful.
ROW_NUMBER
SELECT
employee_id,
department_id,
salary,
ROW_NUMBER() OVER (
PARTITION BY department_id
ORDER BY salary DESC
) AS salary_rank
FROM employees;
This assigns a ranking within each department.
Running total
SELECT
order_date,
total_amount,
SUM(total_amount) OVER (
ORDER BY order_date
) AS running_revenue
FROM daily_orders;
This is useful for dashboards and cumulative metrics.
EXISTS
EXISTS checks whether related rows exist.
SELECT *
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);
This returns customers who have at least one order.
This is often cleaner than some IN patterns in real SQL work.
UNION
UNION combines results from two queries.
UNION removes duplicates
SELECT email
FROM customers
UNION
SELECT email
FROM employees;
UNION ALL keeps duplicates
SELECT email
FROM customers
UNION ALL
SELECT email
FROM employees;
Use UNION ALL when you do not need duplicate removal.
It is often faster and more predictable.
Common date query patterns
Date logic varies by database, but the general ideas are common.
Rows from the last 7 days
SELECT *
FROM orders
WHERE created_at >= CURRENT_DATE - INTERVAL '7 days';
Filter by year or month
Exact syntax differs across engines, but the idea is to avoid unnecessarily wrapping indexed date columns in functions when performance matters.
A better range pattern is often:
SELECT *
FROM orders
WHERE created_at >= '2026-04-01'
AND created_at < '2026-05-01';
This is often clearer and more index-friendly than function-heavy date extraction in hot queries.
Useful string functions
Function names vary slightly by database, but these patterns are common.
Concatenate strings
SELECT first_name || ' ' || last_name AS full_name
FROM users;
Some databases use CONCAT(...) instead.
Lowercase
SELECT LOWER(email)
FROM users;
Uppercase
SELECT UPPER(country)
FROM users;
Trim spaces
SELECT TRIM(email)
FROM users;
These are useful in cleanup and normalization work.
Null handling with COALESCE
COALESCE returns the first non-null value.
SELECT
customer_name,
COALESCE(phone_number, 'No Phone') AS phone_display
FROM customers;
This is one of the most practical SQL functions because null values are everywhere.
Safe query habits for developers
A cheat sheet is not just syntax. It should also reinforce good habits.
1. Avoid SELECT *
Select only the columns you need.
2. Always think about WHERE
A missing filter can turn a safe query into a very expensive one.
3. Be careful with UPDATE and DELETE
Always confirm the WHERE clause.
A safe workflow is:
- run a
SELECTwith the same filter first - verify the rows
- then run the
UPDATEorDELETE
4. Use aliases for readability
Especially in joined queries.
5. Inspect real execution plans when performance matters
SQL that looks fine can still perform badly.
6. Use constraints to protect data quality
Do not rely only on application code for uniqueness and integrity.
Common SQL query structure
A useful mental model for reading and writing queries is:
SELECTFROMJOINWHEREGROUP BYHAVINGORDER BYLIMIT
That is not always the literal execution order inside the engine, but it is a very useful writing and reading model for developers.
A few common real-world query patterns
Get latest 10 records
SELECT id, created_at, status
FROM orders
ORDER BY created_at DESC
LIMIT 10;
Get rows with no related record
SELECT c.*
FROM customers c
LEFT JOIN orders o
ON c.customer_id = o.customer_id
WHERE o.order_id IS NULL;
This finds customers with no orders.
Top 5 customers by revenue
SELECT
customer_id,
SUM(total_amount) AS total_revenue
FROM orders
GROUP BY customer_id
ORDER BY total_revenue DESC
LIMIT 5;
Count rows by category
SELECT
status,
COUNT(*) AS status_count
FROM tickets
GROUP BY status
ORDER BY status_count DESC;
Update rows based on a rule
UPDATE products
SET stock_status =
CASE
WHEN stock_quantity = 0 THEN 'Out of Stock'
WHEN stock_quantity < 10 THEN 'Low Stock'
ELSE 'In Stock'
END;
Find duplicate values
SELECT
email,
COUNT(*) AS duplicate_count
FROM users
GROUP BY email
HAVING COUNT(*) > 1;
SQL patterns worth memorizing
If you only memorize a few SQL patterns, make them these:
Filter rows
SELECT ...
FROM ...
WHERE ...;
Join related tables
SELECT ...
FROM a
JOIN b ON ...;
Aggregate by group
SELECT group_col, COUNT(*)
FROM table_name
GROUP BY group_col;
Filter grouped results
SELECT ...
FROM ...
GROUP BY ...
HAVING ...;
Conditional labeling
CASE
WHEN ... THEN ...
ELSE ...
END
Reusable CTE
WITH cte_name AS (
SELECT ...
)
SELECT ...
FROM cte_name;
These patterns cover a huge amount of practical SQL work.
FAQ
What should a SQL cheat sheet include?
A good SQL cheat sheet should include core query syntax, filtering, joins, grouping, aggregates, CASE expressions, subqueries, CTEs, inserts, updates, deletes, and practical query patterns developers use regularly.
Is SQL syntax the same in every database?
The core SQL syntax is very similar across major databases, but some features, functions, date handling, limit syntax, upsert patterns, and advanced features differ between PostgreSQL, MySQL, SQL Server, SQLite, and others.
What are the most important SQL commands for developers?
The most important SQL commands for developers are SELECT, INSERT, UPDATE, DELETE, JOIN, GROUP BY, ORDER BY, CASE, COUNT, SUM, AVG, and common patterns like CTEs and subqueries.
What is the best way to learn SQL quickly?
The fastest way to learn SQL is to combine a cheat sheet with real table data and practical projects, then repeatedly write queries that use filtering, joins, grouping, and reporting logic.
Final thoughts
The best SQL cheat sheet is not one that lists every keyword in the language. It is one that helps developers do real work faster.
That means remembering the patterns that show up constantly:
- selecting the right data
- filtering clearly
- joining related tables
- grouping and aggregating
- writing conditional logic
- updating safely
- and thinking about performance when queries matter
If you keep coming back to this cheat sheet while building real queries, you will reinforce the parts of SQL that matter most in everyday development.
And that is where SQL really becomes useful: not as a list of commands, but as a practical tool for shaping, understanding, and working with real data.