SELECT & Filtering
SELECT * FROM usersSelect all columns from tableSELECT id, name FROM usersSelect specific columnsSELECT DISTINCT country FROM usersSelect unique valuesSELECT name AS full_name FROM usersColumn aliasSELECT * FROM users WHERE age > 18Filter rows with WHEREWHERE age BETWEEN 18 AND 30Range filter (inclusive)WHERE name LIKE 'A%'Pattern match - % any chars, _ single charWHERE status IN (1, 2, 3)Match any value in listWHERE email IS NULLCheck for NULLWHERE email IS NOT NULLCheck for non-NULLWHERE a = 1 AND b = 2Both conditions must be trueWHERE a = 1 OR b = 2Either condition must be trueWHERE NOT activeNegate conditionORDER BY name ASCSort ascending (default)ORDER BY created_at DESCSort descendingORDER BY country, name ASCSort by multiple columnsLIMIT 10Return at most 10 rowsLIMIT 10 OFFSET 20Pagination: skip 20, take 10Joins
FROM a INNER JOIN b ON a.id = b.a_idINNER JOIN - rows with match in both tablesFROM a LEFT JOIN b ON a.id = b.a_idLEFT JOIN - all rows from left, matched from rightFROM a RIGHT JOIN b ON a.id = b.a_idRIGHT JOIN - all rows from right, matched from leftFROM a FULL OUTER JOIN b ON a.id = b.a_idFULL OUTER JOIN - all rows from both tablesFROM a CROSS JOIN bCartesian product - every row paired with every otherFROM a LEFT JOIN b ON ... WHERE b.id IS NULLRows in a with NO match in bFROM users u JOIN orders o ON u.id = o.user_idTable aliases for readabilityJOIN orders o ON o.user_id = u.id AND o.status = 1Additional condition in JOINAggregation
SELECT COUNT(*) FROM usersCount all rowsSELECT COUNT(email) FROM usersCount non-NULL valuesSELECT COUNT(DISTINCT country) FROM usersCount unique valuesSELECT SUM(amount) FROM ordersSum of column valuesSELECT AVG(price) FROM productsAverage valueSELECT MIN(price), MAX(price) FROM productsMinimum and maximumGROUP BY countryGroup rows sharing the same valueSELECT country, COUNT(*) FROM users GROUP BY countryCount per groupHAVING COUNT(*) > 5Filter on aggregated result (like WHERE for groups)GROUP BY country ORDER BY COUNT(*) DESCSort groups by aggregateSELECT country, SUM(amount) FROM orders GROUP BY country WITH ROLLUPAdd subtotals and grand totalSubqueries
WHERE id IN (SELECT user_id FROM orders)Subquery in WHERE - filter by related dataWHERE id NOT IN (SELECT user_id FROM orders)Exclude rows with match in subqueryWHERE EXISTS (SELECT 1 FROM orders WHERE user_id = u.id)EXISTS - true if subquery returns any rowSELECT (SELECT COUNT(*) FROM o WHERE o.user_id = u.id) FROM users uScalar subquery in SELECTFROM (SELECT ...) AS subDerived table - subquery as FROM sourceWHERE age > (SELECT AVG(age) FROM users)Compare against aggregate subqueryCTEs & Window Functions
WITH cte AS (SELECT ...) SELECT * FROM cteNamed temporary result set (CTE)WITH a AS (...), b AS (...) SELECT * FROM a JOIN bMultiple CTEsWITH RECURSIVE cte AS (...) SELECT * FROM cteRecursive CTE - useful for trees/hierarchiesROW_NUMBER() OVER (ORDER BY created_at)Row number within result setRANK() OVER (ORDER BY score DESC)Rank with gaps for tiesDENSE_RANK() OVER (ORDER BY score DESC)Rank without gaps for tiesROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC)Row number within each partitionSUM(amount) OVER (PARTITION BY user_id)Aggregate per partition without collapsing rowsSUM(amount) OVER (ORDER BY date ROWS UNBOUNDED PRECEDING)Cumulative sum (running total)LAG(amount, 1) OVER (ORDER BY date)Value from previous rowLEAD(amount, 1) OVER (ORDER BY date)Value from next rowFIRST_VALUE(name) OVER (...)First value in window frameNTILE(4) OVER (ORDER BY score)Divide rows into 4 bucketsModifying Data
INSERT INTO users (name, email) VALUES ('Alice', 'a@b.com')Insert a single rowINSERT INTO users (name) VALUES ('Alice'), ('Bob')Insert multiple rowsINSERT INTO archive SELECT * FROM logs WHERE date < "2024-01-01"Insert from SELECTUPDATE users SET status = 1 WHERE id = 5Update matching rowsUPDATE users SET age = age + 1Update with expression (no WHERE = all rows!)DELETE FROM users WHERE id = 5Delete matching rowsDELETE FROM usersDelete all rows (prefer TRUNCATE for full clear)TRUNCATE TABLE logsDelete all rows fast - resets auto-increment, no rollbackINSERT INTO users (id, name) VALUES (1, "Alice") ON DUPLICATE KEY UPDATE name = "Alice"Upsert (MySQL)INSERT INTO users (id, name) VALUES (1, "Alice") ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.nameUpsert (PostgreSQL)REPLACE INTO users (id, name) VALUES (1, "Alice")Delete and re-insert on conflict (MySQL)Schema & DDL
CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ciCreate database with charsetDROP DATABASE mydbDrop databaseUSE mydbSwitch to databaseCREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)Create tableCREATE TABLE IF NOT EXISTS users (...)Create table only if it does not existDROP TABLE usersDrop tableDROP TABLE IF EXISTS usersDrop table if it existsALTER TABLE users ADD COLUMN age INTAdd columnALTER TABLE users DROP COLUMN ageRemove columnALTER TABLE users MODIFY COLUMN name VARCHAR(500)Change column definitionALTER TABLE users RENAME COLUMN name TO full_nameRename columnRENAME TABLE old_name TO new_nameRename tableCREATE TABLE new AS SELECT * FROM oldCreate table from SELECT resultDESCRIBE usersShow column definitions for a tableSHOW TABLESList tables in current databaseSHOW CREATE TABLE usersShow CREATE TABLE statementSHOW INDEX FROM usersList indexes on a tableIndexes & Constraints
PRIMARY KEY (id)Unique, non-null identifier for each rowUNIQUE (email)All values must be unique (NULL allowed)NOT NULLColumn cannot contain NULLDEFAULT 0Value to use when none is suppliedCHECK (age >= 0)Enforce a condition on column valuesFOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADEForeign key with cascade deleteON DELETE SET NULLSet FK column to NULL when parent is deletedON DELETE RESTRICTPrevent deletion if child rows existCREATE INDEX idx_email ON users(email)Create standard indexCREATE UNIQUE INDEX idx_email ON users(email)Create unique indexCREATE INDEX idx_name ON users(last_name, first_name)Composite indexDROP INDEX idx_email ON usersRemove indexEXPLAIN SELECT * FROM users WHERE email = "a@b.com"Show query execution planANALYZE TABLE usersUpdate table statistics for optimizerTransactions
START TRANSACTIONBegin a transaction (also: BEGIN)COMMITPersist all changes in the transactionROLLBACKUndo all changes since START TRANSACTIONSAVEPOINT sp1Set a named savepoint within transactionROLLBACK TO SAVEPOINT sp1Roll back to savepoint without full rollbackRELEASE SAVEPOINT sp1Remove a savepointSET autocommit = 0Disable auto-commit for sessionSET TRANSACTION ISOLATION LEVEL REPEATABLE READSet isolation level: READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLESELECT ... FOR UPDATELock selected rows for update within transactionSELECT ... LOCK IN SHARE MODEShared lock - allows reads, blocks writesFunctions & Expressions
COALESCE(a, b, c)Return first non-NULL argumentNULLIF(a, b)Return NULL if a = b, else return aIFNULL(expr, alt)Return alt if expr is NULL (MySQL)CASE WHEN x > 0 THEN "pos" WHEN x < 0 THEN "neg" ELSE "zero" ENDConditional expressionCAST(price AS DECIMAL(10,2))Cast to data typeCONVERT(name USING utf8mb4)Convert character set (MySQL)CONCAT(first, " ", last)Concatenate stringsCONCAT_WS(", ", city, state)Concatenate with separator, skips NULLsUPPER(name) / LOWER(name)Change caseTRIM(name) / LTRIM / RTRIMRemove whitespaceLENGTH(name)String length in bytesCHAR_LENGTH(name)String length in characters (multi-byte safe)SUBSTRING(str, 1, 3)Extract substring (1-indexed)REPLACE(str, "old", "new")Replace substringNOW()Current datetimeCURDATE() / CURTIME()Current date / current timeDATE_FORMAT(dt, "%Y-%m-%d")Format datetime as stringDATE_ADD(dt, INTERVAL 7 DAY)Add interval to dateDATEDIFF(a, b)Difference in days between two datesYEAR(dt) / MONTH(dt) / DAY(dt)Extract date partsROUND(3.567, 2)Round to 2 decimal placesFLOOR(3.9) / CEIL(3.1)Round down / round upABS(n)Absolute valueMOD(n, m)Modulo - remainder of n / m