📡 You're offline — showing cached content
New version available!
Quick Access
MySQL Reference

MySQL / SQL

Common queries, joins, and database operations.

All Topics

DDL — Schema Design

CREATE TABLE
Define table structure with column types, constraints, and defaults.
Example: CREATE TABLE users (id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, email VARCHAR(255) NOT NULL UNIQUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
Data Types
INT/BIGINT (integers), VARCHAR(n)/TEXT (strings), DECIMAL(p,s) (exact), FLOAT (approx), DATE/DATETIME/TIMESTAMP, BOOLEAN, JSON, ENUM.
Example: price DECIMAL(10,2), active BOOLEAN DEFAULT TRUE, meta JSON
Constraints
PRIMARY KEY, UNIQUE, NOT NULL, DEFAULT, CHECK, FOREIGN KEY. Can be inline or table-level.
Example: CONSTRAINT chk_age CHECK (age >= 0 AND age <= 150)
ALTER TABLE
Add/modify/drop columns, add/drop constraints, rename columns, change data types.
Example: ALTER TABLE users ADD COLUMN avatar VARCHAR(500), DROP COLUMN old_field, MODIFY email VARCHAR(320);
CREATE INDEX
Speed up queries on frequently searched columns. Composite indexes obey left-prefix rule.
Example: CREATE INDEX idx_user_email ON users(email); CREATE INDEX idx_comp ON orders(user_id, status, created_at);
FOREIGN KEY ... ON DELETE
Referential integrity. ON DELETE CASCADE/SET NULL/RESTRICT controls child row behavior.
Example: FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ON UPDATE RESTRICT
Partial / Functional indexes
Index only rows matching a condition (PostgreSQL) or expression result — smaller, faster indexes.
Example: CREATE INDEX idx_active ON users(email) WHERE active = TRUE; -- PostgreSQL

SELECT & Filtering

SELECT DISTINCT
Remove duplicate rows from result set. Works on combination of all selected columns.
Example: SELECT DISTINCT city, country FROM users ORDER BY country;
WHERE vs HAVING
WHERE filters ROWS before grouping. HAVING filters GROUPS after GROUP BY. Can't use aliases in WHERE.
Example: SELECT dept, COUNT(*) cnt FROM emp GROUP BY dept HAVING cnt > 5;
LIKE / ILIKE / REGEXP
% = any chars, _ = one char. ILIKE is case-insensitive (PostgreSQL). REGEXP for patterns (MySQL).
Example: WHERE email LIKE '%@gmail.com' AND name REGEXP '^[A-Z]'
IN / NOT IN / EXISTS
IN compares to list/subquery. EXISTS is faster than IN for correlated subqueries (stops at first match).
Example: WHERE id IN (1,2,3) -- or -- WHERE EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id)
BETWEEN ... AND
Inclusive range check. Works on numbers, dates, strings. Equivalent to >= AND <=.
Example: WHERE created_at BETWEEN '2024-01-01' AND '2024-12-31'
COALESCE / NULLIF / IFNULL
COALESCE returns first non-null. NULLIF returns null if args equal. IFNULL is MySQL's 2-arg COALESCE.
Example: SELECT COALESCE(nickname, full_name, email) AS display_name FROM users;
CASE WHEN ... END
SQL's if/else — use in SELECT, WHERE, ORDER BY, aggregate functions.
Example: SELECT CASE WHEN score>=90 THEN 'A' WHEN score>=70 THEN 'B' ELSE 'C' END grade FROM students;
LIMIT / OFFSET / FETCH NEXT
Pagination. OFFSET is slow on large tables — use keyset pagination (WHERE id > last_id) instead.
Example: SELECT * FROM posts ORDER BY id DESC LIMIT 20 OFFSET 40;

JOINs

INNER JOIN
Returns only rows where join condition matches in BOTH tables — null-safe intersection.
Example: SELECT p.title, u.name FROM posts p INNER JOIN users u ON p.user_id = u.id;
LEFT (OUTER) JOIN
All rows from left table, matching rows from right (NULLs where no match). Most common join type.
Example: SELECT u.name, COUNT(o.id) orders FROM users u LEFT JOIN orders o ON o.user_id=u.id GROUP BY u.id;
FULL OUTER JOIN
All rows from both tables — NULLs where no match on either side. (PostgreSQL native; MySQL simulate with UNION).
Example: SELECT * FROM a FULL OUTER JOIN b ON a.id=b.a_id;
CROSS JOIN
Cartesian product — every row of A combined with every row of B. Use for generating combinations.
Example: SELECT s.size, c.color FROM sizes s CROSS JOIN colors c;
Self JOIN
Join a table to itself using aliases — hierarchies, managers, related records in same table.
Example: SELECT e.name, m.name manager FROM employees e LEFT JOIN employees m ON e.manager_id=m.id;
JOIN with USING(col)
Cleaner syntax when join column has same name in both tables — no table prefix needed in SELECT.
Example: SELECT * FROM orders JOIN users USING(user_id);

Aggregation & Window Functions

GROUP BY + aggregate
COUNT, SUM, AVG, MIN, MAX, GROUP_CONCAT. COUNT(*) counts rows; COUNT(col) skips NULLs.
Example: SELECT category, COUNT(*), AVG(price), MAX(price) FROM products GROUP BY category;
ROLLUP / CUBE / GROUPING SETS
Generate sub-totals and grand totals in a single query — reporting/pivot use cases.
Example: SELECT dept, job, SUM(sal) FROM emp GROUP BY ROLLUP(dept, job);
ROW_NUMBER() OVER()
Window function — sequential number per partition without collapsing rows.
Example: SELECT *, ROW_NUMBER() OVER(PARTITION BY dept ORDER BY salary DESC) rn FROM employees;
RANK() / DENSE_RANK()
RANK skips numbers after ties; DENSE_RANK doesn't. PERCENT_RANK gives relative position 0-1.
Example: RANK() OVER(ORDER BY score DESC) -- ties get same rank, next skips
LAG() / LEAD()
Access values from previous/next rows without a self-join. Specify offset and default.
Example: LAG(revenue, 1, 0) OVER(PARTITION BY region ORDER BY month) prev_revenue
SUM ... OVER (running total)
Running/cumulative sum using ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW frame.
Example: SUM(amount) OVER(ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
NTILE(n)
Divide result set into N buckets/quantiles — quartiles, deciles, percentiles.
Example: NTILE(4) OVER(ORDER BY score DESC) AS quartile

CTEs & Subqueries

WITH cte AS (...)
Common Table Expression — named subquery, reusable in the same statement. Improves readability.
Example: WITH top10 AS (SELECT * FROM products ORDER BY sales DESC LIMIT 10) SELECT * FROM top10 WHERE price > 100;
Recursive CTE
WITH RECURSIVE traverses hierarchical data (trees, graphs) without application-level recursion.
Example: WITH RECURSIVE tree AS (SELECT * FROM cats WHERE parent IS NULL UNION ALL SELECT c.* FROM cats c JOIN tree t ON c.parent=t.id) SELECT * FROM tree;
Correlated subquery
Inner query references outer query — runs once per outer row. Usually replaceable with JOIN for performance.
Example: SELECT * FROM orders o WHERE amount = (SELECT MAX(amount) FROM orders WHERE user_id=o.user_id);
Lateral JOIN / CROSS APPLY
Subquery can reference columns from preceding FROM items — like a correlated subquery in FROM clause.
Example: FROM users, LATERAL (SELECT * FROM orders WHERE user_id=users.id ORDER BY date DESC LIMIT 3) recent

Performance & Transactions

EXPLAIN / EXPLAIN ANALYZE
Show query execution plan — identify full table scans, missing indexes, expensive sorts.
Example: EXPLAIN ANALYZE SELECT * FROM orders WHERE status='pending';
Index selectivity
High-cardinality columns (email, UUID) benefit most from indexes. Low-cardinality (boolean) often don't help.
Example: SELECT COUNT(DISTINCT status)/COUNT(*) selectivity FROM orders; -- low = bad index candidate
BEGIN / COMMIT / ROLLBACK
Transaction block — all-or-nothing execution. SAVEPOINT creates partial rollback points.
Example: BEGIN; UPDATE accounts SET balance=balance-100 WHERE id=1; UPDATE accounts SET balance=balance+100 WHERE id=2; COMMIT;
Isolation levels
READ UNCOMMITTED → READ COMMITTED → REPEATABLE READ → SERIALIZABLE. Trade-off: concurrency vs consistency.
Example: SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
UPSERT: INSERT ... ON CONFLICT
Insert or update atomically. MySQL uses ON DUPLICATE KEY UPDATE; PostgreSQL uses ON CONFLICT DO UPDATE.
Example: INSERT INTO hits(date,count) VALUES(TODAY,1) ON DUPLICATE KEY UPDATE count=count+1;
Covering index
Index that contains all columns needed by query — eliminates table row lookup entirely.
Example: CREATE INDEX idx_cover ON orders(user_id, status) INCLUDE (amount, created_at);
JSON functions
Query JSON columns: JSON_EXTRACT ($path), ->> operator, JSON_ARRAYAGG, JSON_OBJECTAGG.
Example: SELECT meta->>'$.color' FROM products WHERE meta->>'$.size' = 'L';