SQL Syntax Cheatsheet
Complete SQL reference — queries, filtering, joins, data modification, table operations, aggregates, and advanced patterns like CTEs and window functions.
Data Query
SELECT col1, col2 FROM tableRetrieve specific columns from a tableSELECT * FROM tableRetrieve all columns from a tableSELECT DISTINCT col FROM tableReturn only unique values for a columnSELECT col AS alias FROM tableRename a column in the result setSELECT * FROM table WHERE conditionFilter rows based on a conditionSELECT * FROM t1 JOIN t2 ON t1.id = t2.fkCombine rows from two tables on a matching keySELECT col, COUNT(*) FROM table GROUP BY colGroup rows by column and aggregateGROUP BY col HAVING COUNT(*) > 1Filter groups after aggregationSELECT * FROM table ORDER BY col ASCSort results ascending (ASC) or descending (DESC)SELECT * FROM table LIMIT 10Return only the first N rowsSELECT * FROM table LIMIT 10 OFFSET 20Skip 20 rows, then return the next 10SELECT ... UNION SELECT ...Combine results of two queries, removing duplicatesSELECT ... UNION ALL SELECT ...Combine results of two queries, keeping duplicatesFiltering & Operators
WHERE col = valueEqual toWHERE col != value / col <> valueNot equal toWHERE col > valueGreater thanWHERE col < valueLess thanWHERE col >= valueGreater than or equal toWHERE col <= valueLess than or equal toWHERE col BETWEEN 10 AND 50Value within an inclusive rangeWHERE col IN ('a', 'b', 'c')Value matches any item in a listWHERE col NOT IN (subquery)Value does not match any itemWHERE col LIKE 'pat%'Pattern match — % is any characters, _ is one characterWHERE col ILIKE 'pat%'Case-insensitive LIKE (PostgreSQL)WHERE col IS NULLCheck if value is NULLWHERE col IS NOT NULLCheck if value is not NULLWHERE cond1 AND cond2Both conditions must be trueWHERE cond1 OR cond2At least one condition must be trueWHERE NOT conditionNegate a conditionWHERE EXISTS (subquery)True if the subquery returns any rowsJoins
FROM t1 INNER JOIN t2 ON t1.id = t2.fkReturn rows that have matching values in both tablesFROM t1 LEFT JOIN t2 ON t1.id = t2.fkAll rows from left table, matched rows from right (NULLs if no match)FROM t1 RIGHT JOIN t2 ON t1.id = t2.fkAll rows from right table, matched rows from left (NULLs if no match)FROM t1 FULL OUTER JOIN t2 ON t1.id = t2.fkAll rows from both tables, NULLs where no match existsFROM t1 CROSS JOIN t2Cartesian product — every row in t1 paired with every row in t2FROM employees e1 JOIN employees e2 ON e1.mgr = e2.idSelf join — join a table to itself using aliasesFROM t1 LEFT JOIN t2 ON ... WHERE t2.id IS NULLAnti-join — find rows in t1 with no match in t2FROM t1 NATURAL JOIN t2Join on all columns with the same name (use with caution)FROM t1 JOIN t2 USING (col)Join on a shared column name without specifying both table prefixesData Modification
INSERT INTO table (col1, col2) VALUES (v1, v2)Insert a single rowINSERT INTO table (col1, col2) VALUES (v1, v2), (v3, v4)Insert multiple rows at onceINSERT INTO table SELECT ... FROM other_tableInsert rows from another queryUPDATE table SET col = value WHERE conditionUpdate specific rows matching a conditionUPDATE t1 SET col = t2.val FROM t2 WHERE t1.id = t2.fkUpdate using values from another table (PostgreSQL)DELETE FROM table WHERE conditionDelete specific rows matching a conditionDELETE FROM tableDelete all rows (logged, can rollback)INSERT ... ON CONFLICT (col) DO UPDATE SET ...Upsert — insert or update on conflict (PostgreSQL)INSERT ... ON DUPLICATE KEY UPDATE ...Upsert — insert or update on duplicate (MySQL)MERGE INTO target USING source ON ... WHEN MATCHED THEN ...MERGE / upsert (SQL Server, Oracle)RETURNING *Return affected rows after INSERT/UPDATE/DELETE (PostgreSQL)Table Operations
CREATE TABLE t (id INT PRIMARY KEY, name VARCHAR(100))Create a new table with columns and a primary keyCREATE TABLE t (... , CONSTRAINT fk FOREIGN KEY (col) REFERENCES other(id))Create table with a foreign key constraintCREATE TABLE IF NOT EXISTS t (...)Create table only if it doesn't already existCREATE TABLE new_t AS SELECT * FROM old_tCreate a new table from a query resultALTER TABLE t ADD COLUMN col TYPEAdd a new column to an existing tableALTER TABLE t DROP COLUMN colRemove a column from a tableALTER TABLE t RENAME COLUMN old TO newRename a columnALTER TABLE t ALTER COLUMN col SET NOT NULLAdd a NOT NULL constraintALTER TABLE t ADD CONSTRAINT uq UNIQUE (col)Add a unique constraintDROP TABLE tPermanently delete a table and all its dataDROP TABLE IF EXISTS tDrop table only if it exists (avoids errors)TRUNCATE TABLE tRemove all rows quickly (not logged, cannot rollback in some DBs)CREATE INDEX idx ON t (col)Create an index to speed up queries on a columnCREATE UNIQUE INDEX idx ON t (col)Create a unique index (enforces uniqueness)DROP INDEX idxRemove an indexAggregate Functions
COUNT(*)Count total number of rowsCOUNT(col)Count non-NULL values in a columnCOUNT(DISTINCT col)Count unique non-NULL valuesSUM(col)Sum of all values in a columnAVG(col)Average of all values in a columnMIN(col)Smallest value in a columnMAX(col)Largest value in a columnGROUP_CONCAT(col SEPARATOR ', ')Concatenate grouped values into a string (MySQL)STRING_AGG(col, ', ')Concatenate grouped values into a string (PostgreSQL)ARRAY_AGG(col)Aggregate values into an array (PostgreSQL)Common Patterns
SELECT * FROM t WHERE col IN (SELECT col FROM t2)Subquery — use a query result as a filterSELECT *, (SELECT MAX(val) FROM t2) AS m FROM t1Scalar subquery — returns a single valueWITH cte AS (SELECT ... FROM t) SELECT * FROM cteCommon Table Expression (CTE) — named temporary result setWITH RECURSIVE cte AS (base UNION ALL SELECT ... FROM cte WHERE ...)Recursive CTE — traverse hierarchies or generate seriesCASE WHEN cond THEN val WHEN cond2 THEN val2 ELSE default ENDConditional logic inside a queryCOALESCE(col1, col2, 'default')Return the first non-NULL value from a listNULLIF(a, b)Return NULL if a equals b, otherwise return aCAST(col AS INTEGER) / col::INTEGERConvert a value to a different data typeROW_NUMBER() OVER (ORDER BY col)Window function — assign sequential row numbersRANK() OVER (PARTITION BY grp ORDER BY col)Window function — rank within groups (gaps on ties)DENSE_RANK() OVER (ORDER BY col)Window function — rank without gaps on tiesSUM(col) OVER (PARTITION BY grp ORDER BY date)Running total — cumulative sum within a groupLAG(col, 1) OVER (ORDER BY date)Access the value from the previous rowLEAD(col, 1) OVER (ORDER BY date)Access the value from the next rowFAQ
What is the difference between WHERE and HAVING?
WHERE filters individual rows before grouping — it cannot reference aggregate functions. HAVING filters groups after GROUP BY has been applied, so you can use aggregates like COUNT(*) > 5 in a HAVING clause but not in WHERE.
When should I use a CTE vs a subquery?
CTEs (WITH ... AS) improve readability for complex queries and can be referenced multiple times. Subqueries are fine for simple, one-off filters. Use CTEs when the query is deeply nested, when you need to reuse the same derived table, or when building recursive queries.
What is the difference between DELETE and TRUNCATE?
DELETE removes rows one at a time, is fully logged, supports WHERE clauses, and fires triggers. TRUNCATE deallocates entire data pages at once, is much faster, but cannot filter rows and may not be rolled back in some databases. Use DELETE for targeted removal and TRUNCATE to quickly empty an entire table.
How do window functions differ from GROUP BY?
GROUP BY collapses rows into one row per group, losing individual row detail. Window functions (OVER clause) perform calculations across related rows while preserving every original row in the result. This lets you compute running totals, rankings, and moving averages without grouping.