SQL Cheatsheet

View saved

SQL asks a database questions (and sometimes changes rows). Start with SELECT, then add filters, sorts, and joins as your questions get more specific.

Practice on a disposable database first. Prefer precise WHERE clauses before UPDATE or DELETE, and use transactions when a change must succeed or fail as a unit.

Basics

What SQL does

Structured Query Language describes what data you want; the database engine figures out how to fetch or change it.

-- Ask for rows matching a condition
SELECT name FROM users WHERE active = 1;

Tables and rows

A table is a named grid: columns define fields, each row is one record.

CREATE TABLE users (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  email TEXT
);

Primary key

A column (or set of columns) that uniquely identifies each row—often an auto-incrementing id.

CREATE TABLE posts (
  id INTEGER PRIMARY KEY,
  title TEXT NOT NULL
);

NULL

Means “unknown or missing,” not the number zero or an empty string. Compare with IS NULL / IS NOT NULL.

SELECT * FROM users WHERE email IS NULL;

Comments

Document queries with -- line comments or /* ... */ blocks.

-- Count active accounts
SELECT COUNT(*) FROM users WHERE active = 1;

Reading data

SELECT *

Return every column. Fine for exploration; prefer naming columns in real apps.

SELECT * FROM users;

SELECT columns

List only the fields you need—clearer and often faster.

SELECT id, name, email FROM users;

DISTINCT

Remove duplicate values from the result set for the selected columns.

SELECT DISTINCT city FROM customers;

Aliases (AS)

Rename a column or table in the result for readability.

SELECT name AS full_name, created_at AS joined
FROM users;

LIMIT

Cap how many rows come back—great while testing or paging results.

SELECT * FROM products ORDER BY price DESC LIMIT 10;

Filtering & sorting

WHERE

Keep only rows that match a condition.

SELECT * FROM orders WHERE status = 'shipped';

AND / OR / NOT

Combine or negate conditions. Use parentheses when mixing AND and OR.

SELECT * FROM products
WHERE in_stock = 1 AND (price < 20 OR on_sale = 1);

LIKE

Match text patterns. % means any sequence; _ means one character.

SELECT * FROM users WHERE email LIKE '%@example.com';

BETWEEN / IN

BETWEEN is inclusive range; IN matches any value in a list.

SELECT * FROM products WHERE price BETWEEN 10 AND 50;
SELECT * FROM orders WHERE status IN ('new', 'paid');

ORDER BY

Sort results ascending (ASC, default) or descending (DESC).

SELECT name, score FROM players ORDER BY score DESC, name ASC;

Aggregate + GROUP BY

Summarize groups with COUNT, SUM, AVG, and friends.

SELECT city, COUNT(*) AS customers
FROM customers
GROUP BY city
ORDER BY customers DESC;

Joins

Why join

Combine rows from related tables using a shared key (for example user_id).

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

INNER JOIN

Return only rows that have a match in both tables.

SELECT p.title, c.name AS category
FROM posts p
INNER JOIN categories c ON c.id = p.category_id;

LEFT JOIN

Keep every row from the left table; missing right-side matches show as NULL.

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

Foreign key idea

A column that points at another table’s primary key keeps relationships honest.

CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  user_id INTEGER NOT NULL,
  total REAL,
  FOREIGN KEY (user_id) REFERENCES users(id)
);

Self-check after join

If row counts explode, you may have matched too loosely—inspect join keys and sample results.

SELECT COUNT(*) FROM orders;
SELECT COUNT(*) FROM users u
INNER JOIN orders o ON o.user_id = u.id;

Changing data

INSERT

Add one or more new rows. List columns explicitly when you can.

INSERT INTO users (name, email)
VALUES ('Sam Lee', '[email protected]');

UPDATE

Change existing rows. Always include a careful WHERE unless you truly mean every row.

UPDATE users SET active = 0 WHERE last_login < '2024-01-01';

DELETE

Remove matching rows. Preview with SELECT using the same WHERE first.

SELECT * FROM sessions WHERE expires_at < CURRENT_TIMESTAMP;
DELETE FROM sessions WHERE expires_at < CURRENT_TIMESTAMP;

CREATE TABLE

Define a new table and its columns before you insert data.

CREATE TABLE tags (
  id INTEGER PRIMARY KEY,
  label TEXT UNIQUE NOT NULL
);

Transactions

Group statements so they all commit together or all roll back on failure.

BEGIN;
UPDATE accounts SET balance = balance - 50 WHERE id = 1;
UPDATE accounts SET balance = balance + 50 WHERE id = 2;
COMMIT;

Design tips

Normalize lightly

Store each fact once (users in one table, orders in another) and link with keys instead of copying names everywhere.

-- Prefer user_id on orders, not repeating full user name on every order row

Useful indexes

Index columns you filter or join on often. Too many indexes slow writes.

CREATE INDEX idx_orders_user_id ON orders(user_id);

Views

Save a named query you reuse often without duplicating SQL in every app call.

CREATE VIEW active_users AS
SELECT id, name, email FROM users WHERE active = 1;

Avoid SELECT * in apps

Name columns so schema changes and large text fields do not surprise your code.

SELECT id, name FROM users WHERE id = 42;

Back up before bulk edits

Export or snapshot before mass UPDATE/DELETE. Mistakes are cheap to undo from a backup.

-- SQLite example
.backup main backup.db

Comments

One comment per signed-in account. Comments are saved with this page’s URL.