Skip to content
Codeloom

Cheat Sheets

SQL Cheat Sheet

SQL essentials — SELECT, JOIN, GROUP BY, window functions, subqueries, indexes, and schema operations in one quick-reference page.

7 sections 25 snippets

SELECT Basics

Select columns
SELECT name, email FROM users;
SELECT * FROM users;
SELECT DISTINCT category FROM products;
Filtering
SELECT * FROM users WHERE age > 18;
SELECT * FROM users WHERE name LIKE 'A%';
SELECT * FROM users WHERE age BETWEEN 18 AND 30;
SELECT * FROM users WHERE country IN ('US', 'UK', 'CA');
SELECT * FROM users WHERE email IS NOT NULL;
Sorting and limiting
SELECT * FROM users ORDER BY created_at DESC;
SELECT * FROM users ORDER BY name ASC LIMIT 10;
SELECT * FROM users LIMIT 10 OFFSET 20;
Aliases
SELECT first_name AS name, COUNT(*) AS total
FROM users u
JOIN orders o ON u.id = o.user_id;

JOINs

INNER JOIN
SELECT u.name, o.total
FROM users u
JOIN orders o ON u.id = o.user_id;
LEFT JOIN
SELECT u.name, o.total
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;

Returns all users even if they have no orders

Multiple joins
SELECT u.name, o.id, p.name
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN products p ON o.product_id = p.id;
Self join
SELECT e.name, m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;

Aggregations

Aggregate functions
SELECT COUNT(*), SUM(total), AVG(total),
       MIN(total), MAX(total)
FROM orders;
GROUP BY
SELECT category, COUNT(*) AS cnt
FROM products
GROUP BY category
ORDER BY cnt DESC;
HAVING
SELECT user_id, SUM(total) AS spent
FROM orders
GROUP BY user_id
HAVING SUM(total) > 1000;

Window Functions

ROW_NUMBER
SELECT name, salary,
  ROW_NUMBER() OVER (ORDER BY salary DESC) AS rank
FROM employees;
RANK and DENSE_RANK
SELECT name, dept, salary,
  RANK() OVER (PARTITION BY dept ORDER BY salary DESC)
FROM employees;
Running total
SELECT date, amount,
  SUM(amount) OVER (ORDER BY date) AS running_total
FROM transactions;
LAG and LEAD
SELECT date, revenue,
  LAG(revenue) OVER (ORDER BY date) AS prev_revenue,
  revenue - LAG(revenue) OVER (ORDER BY date) AS change
FROM daily_stats;

Subqueries and CTEs

Subquery in WHERE
SELECT * FROM users
WHERE id IN (
  SELECT user_id FROM orders WHERE total > 100
);
CTE (Common Table Expression)
WITH high_spenders AS (
  SELECT user_id, SUM(total) AS spent
  FROM orders
  GROUP BY user_id
  HAVING SUM(total) > 1000
)
SELECT u.name, hs.spent
FROM users u
JOIN high_spenders hs ON u.id = hs.user_id;
Recursive CTE
WITH RECURSIVE org AS (
  SELECT id, name, manager_id, 0 AS depth
  FROM employees WHERE manager_id IS NULL
  UNION ALL
  SELECT e.id, e.name, e.manager_id, o.depth + 1
  FROM employees e JOIN org o ON e.manager_id = o.id
)
SELECT * FROM org;

Schema Operations

Create table
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  email VARCHAR(255) UNIQUE NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);
Alter table
ALTER TABLE users ADD COLUMN age INT;
ALTER TABLE users DROP COLUMN age;
ALTER TABLE users RENAME COLUMN name TO full_name;
Create index
CREATE INDEX idx_users_email ON users(email);
CREATE UNIQUE INDEX idx_users_username ON users(username);

Insert, Update, Delete

Insert
INSERT INTO users (name, email)
VALUES ('Alice', 'alice@example.com');

INSERT INTO users (name, email) VALUES
  ('Bob', 'bob@example.com'),
  ('Carol', 'carol@example.com');
Update
UPDATE users SET name = 'Alice B.' WHERE id = 1;
UPDATE products SET price = price * 1.1
WHERE category = 'electronics';
Delete
DELETE FROM users WHERE id = 1;
DELETE FROM sessions WHERE expires_at < NOW();
Upsert
INSERT INTO users (email, name) VALUES ('a@b.com', 'Alice')
ON CONFLICT (email)
DO UPDATE SET name = EXCLUDED.name;