SELECT * FROM users Select all columns from table
SELECT name, email FROM users Select specific columns
SELECT DISTINCT status FROM orders Select unique values only
SELECT * FROM users WHERE age > 18 Filter rows with WHERE
SELECT * FROM users WHERE name LIKE '%john%' Pattern matching with LIKE
SELECT * FROM users WHERE age BETWEEN 18 AND 30 Range filter with BETWEEN
SELECT * FROM users WHERE status IN ('active', 'pending') Filter by set of values
SELECT * FROM users WHERE email IS NOT NULL Filter null/not null values
SELECT * FROM users ORDER BY name ASC Sort results ascending
SELECT * FROM users ORDER BY created_at DESC LIMIT 10 Get top N rows
SELECT * FROM users LIMIT 10 OFFSET 20 Pagination with offset
SELECT name AS full_name FROM users Column alias
INSERT INTO users (name, email)
VALUES ('John', 'john@mail.com') Insert a row
INSERT INTO users (name, email)
VALUES
('A', 'a@m.com'),
('B', 'b@m.com') Insert multiple rows
UPDATE users SET name = 'Jane'
WHERE id = 1 Update rows matching condition
DELETE FROM users WHERE id = 1 Delete rows matching condition
TRUNCATE TABLE users Delete all rows (fast, no rollback)
INSERT INTO target SELECT * FROM source
WHERE condition Insert from another query
INSERT INTO users (name, email)
VALUES ('John', 'j@m.com')
ON CONFLICT (email) DO UPDATE
SET name = EXCLUDED.name Upsert (insert or update)
SELECT * FROM users
INNER JOIN orders ON users.id = orders.user_id Inner join (matching rows only)
SELECT * FROM users
LEFT JOIN orders ON users.id = orders.user_id Left join (all from left table)
SELECT * FROM users
RIGHT JOIN orders ON users.id = orders.user_id Right join (all from right table)
SELECT * FROM users
FULL OUTER JOIN orders ON users.id = orders.user_id Full outer join (all from both)
SELECT * FROM users
CROSS JOIN products Cross join (cartesian product)
SELECT a.name, b.name
FROM employees a
JOIN employees b ON a.manager_id = b.id Self join
SELECT COUNT(*) FROM users Count all rows
SELECT COUNT(DISTINCT status) FROM orders Count distinct values
SELECT SUM(amount) FROM orders Sum of column values
SELECT AVG(age) FROM users Average of column values
SELECT MIN(price), MAX(price) FROM products Min and max values
SELECT status, COUNT(*)
FROM orders
GROUP BY status Group and count
SELECT status, COUNT(*)
FROM orders
GROUP BY status
HAVING COUNT(*) > 5 Filter groups with HAVING
SELECT department,
STRING_AGG(name, ', ') AS members
FROM employees
GROUP BY department Concatenate grouped values
SELECT * FROM users
WHERE id IN (
SELECT user_id FROM orders
) Subquery with IN
SELECT * FROM users
WHERE EXISTS (
SELECT 1 FROM orders
WHERE orders.user_id = users.id
) Subquery with EXISTS
SELECT *, (
SELECT COUNT(*)
FROM orders
WHERE orders.user_id = users.id
) AS order_count
FROM users Correlated subquery in SELECT
WITH active_users AS (
SELECT * FROM users
WHERE status = 'active'
)
SELECT * FROM active_users Common Table Expression (CTE)
SELECT * FROM (
SELECT *, ROW_NUMBER()
OVER (ORDER BY score DESC) AS rank
FROM students
) ranked
WHERE rank <= 10 Subquery with window function
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE,
created_at TIMESTAMP DEFAULT NOW()
) Create table with constraints
ALTER TABLE users ADD COLUMN age INT Add column to table
ALTER TABLE users DROP COLUMN age Remove column from table
ALTER TABLE users
ALTER COLUMN name SET NOT NULL Modify column constraint
DROP TABLE IF EXISTS users Drop table if it exists
ALTER TABLE users
RENAME COLUMN name TO full_name Rename column
ALTER TABLE orders
ADD FOREIGN KEY (user_id)
REFERENCES users(id)
ON DELETE CASCADE Add foreign key constraint
COALESCE(val1, val2, 'default') Return first non-null value
CASE
WHEN age < 18 THEN 'minor'
WHEN age < 65 THEN 'adult'
ELSE 'senior'
END Conditional expression
CAST(price AS INTEGER) Type casting
CONCAT(first_name, ' ', last_name) Concatenate strings
UPPER(name) / LOWER(name) Convert case
LENGTH(name) / TRIM(name) String length / trim whitespace
NOW() / CURRENT_DATE / CURRENT_TIMESTAMP Current date/time
EXTRACT(YEAR FROM created_at) Extract part from date
DATE_TRUNC('month', created_at) Truncate date to precision
CREATE INDEX idx_users_email
ON users(email) Create index on column
CREATE UNIQUE INDEX idx_users_email
ON users(email) Create unique index
CREATE INDEX idx_name_email
ON users(name, email) Composite index (multi-column)
DROP INDEX idx_users_email Remove an index
EXPLAIN ANALYZE
SELECT * FROM users WHERE email = 'x' Analyze query execution plan
SQL Cheat Sheet — SELECT, JOIN, GROUP BY & Window Functions Reference
SQL was standardized in 1986 and the core dialect — SELECT, JOIN, GROUP BY, INSERT, UPDATE, DELETE — has barely shifted in forty years; CTEs and window functions sit on top of the same shape. The cheatsheet below covers 60+ queries across selects, modifications, joins, aggregates, subqueries, DDL, functions, and indexes. Most trouble in real queries doesn't come from forgetting syntax. It comes from quirks that look ordinary but bite. `NULL` is not equal to anything — including itself — so `WHERE column = NULL` returns no rows and the correct form is `IS NULL`. A `LEFT JOIN` followed by a `WHERE` filtering on the right table silently turns into an `INNER JOIN`, because any row with `NULL` on the right fails the filter. `COUNT(*)` and `COUNT(column)` return different numbers on identical-looking queries. The snippets below are the ones an author reaches for when debugging exactly that — the join that preserves the left side, the `HAVING` that filters groups not rows, the CTE that flattens a nested subquery — without rereading the SQL reference from line one each time.
Common pitfalls in SQL
A few patterns earn their place on the first screen of any SQL file. `WHERE` filters rows before grouping; `HAVING` filters groups after aggregation — putting `COUNT(*) > 5` in `WHERE` raises an error. Joining tables changes row counts: a one-to-many join multiplies, and a `SUM` over the joined result can double-count if the same parent row matches several children. Indexes pay off on columns that appear in `WHERE`, `JOIN ON`, or `ORDER BY` when selectivity is high enough to offset the write cost on every insert; `EXPLAIN ANALYZE` is the way to confirm an index is actually being used. `LIKE 'pattern%'` can use a btree index when the wildcard is at the end, but `LIKE '%pattern'` cannot, so leading wildcards force a full table scan. Transactions and isolation levels matter: the default `READ COMMITTED` lets two reads inside the same transaction see different values, fine for most reports but wrong for balance checks. The cheatsheet groups all of this into Select, Modify, Joins, Aggregate, Subqueries, DDL, Functions, and Indexes so the right section is one click away.
- 60+ practical SQL queries
- 8 categories from SELECT to indexes
- Covers JOINs, CTEs, and window functions
- DDL statements for table management
- One-click copy to clipboard
- PostgreSQL-compatible syntax
Free. No signup. Your inputs stay in your browser. Ads via Google AdSense (consent required).