SQL Server Cheat Sheet
Every T-SQL pattern you reach for daily, on one page: data types, string/date functions, joins/CTEs, window functions, APPLY operators, MERGE, TRY...CATCH, index types, and isolation levels. Bookmark it, or work through the underlying concepts in the free SQL Server tutorial.
Common data types
| Type | Stores |
|---|---|
| INT / BIGINT / SMALLINT / TINYINT | whole numbers of increasing range |
| DECIMAL(p,s) / NUMERIC(p,s) | exact fixed-point numbers |
| FLOAT / REAL | approximate floating-point numbers |
| VARCHAR(n) / NVARCHAR(n) / CHAR(n) | variable/fixed-length strings -- N-prefixed types store Unicode |
| DATE / TIME / DATETIME2 / DATETIMEOFFSET | date and time values, with or without a time zone offset |
| BIT | boolean-like: 0, 1, or NULL |
| UNIQUEIDENTIFIER | a GUID value |
| VARCHAR(MAX) / NVARCHAR(MAX) | large variable-length text, beyond the normal 8000-byte row limit |
String functions
| Function | Purpose | Example |
|---|---|---|
| + | concatenation (NULL propagates) | first_name + ' ' + last_name |
| CONCAT(a, b, ...) | concatenation, NULL-safe | CONCAT('Hi ', name) |
| LEN(s) | character count | LEN('sqlserver') -> 9 |
| SUBSTRING(s, start, len) | extract a substring | SUBSTRING('sqlserver', 1, 3) -> 'sql' |
| UPPER(s) / LOWER(s) | case conversion | UPPER('sql') -> 'SQL' |
| LTRIM(s) / RTRIM(s) / TRIM(s) | remove leading/trailing spaces | TRIM(' hi ') -> 'hi' |
| REPLACE(s, old, new) | substring replacement | REPLACE('2026','20','19') -> '1926' |
| CHARINDEX(sub, s) | 1-based position of a substring | CHARINDEX('server','sqlserver') -> 4 |
| STRING_AGG(col, sep) | concatenate rows into one string | STRING_AGG(name, ', ') |
Date & time functions
| Function | Purpose | Example |
|---|---|---|
| GETDATE() / SYSDATETIME() | current date and time | SELECT GETDATE(); |
| CAST(GETDATE() AS DATE) | current date only | SELECT CAST(GETDATE() AS DATE); |
| DATEDIFF(unit, d1, d2) | difference between two dates in a unit | DATEDIFF(day, hire_date, GETDATE()) |
| DATEADD(unit, n, date) | add/subtract an interval | DATEADD(month, 3, order_date) |
| DATEPART(unit, date) | extract part of a date as an integer | DATEPART(year, order_date) |
| FORMAT(date, fmt) | format a date as text | FORMAT(GETDATE(), 'yyyy-MM-dd') |
| EOMONTH(date) | last day of the month | EOMONTH(order_date) |
Joins & CTEs
| Pattern | Syntax |
|---|---|
| Inner join | FROM a JOIN b ON a.id = b.id |
| Left join | FROM a LEFT JOIN b ON a.id = b.id |
| Full outer join | FROM a FULL OUTER JOIN b ON a.id = b.id |
| Common table expression | WITH recent AS (SELECT * FROM orders WHERE order_date > DATEADD(day,-7,GETDATE())) SELECT * FROM recent; |
| Recursive CTE | WITH tree AS (base case UNION ALL recursive case) SELECT * FROM tree OPTION (MAXRECURSION 100); |
Window functions
| Function | Purpose |
|---|---|
| ROW_NUMBER() OVER (...) | unique sequential number per row |
| RANK() / DENSE_RANK() OVER (...) | rank with / without gaps after ties |
| LAG(col, n) / LEAD(col, n) OVER (...) | value n rows before / after the current row |
| SUM/AVG/COUNT(...) OVER (PARTITION BY ...) | grouped aggregate without collapsing rows |
| NTILE(n) OVER (...) | split rows into n roughly equal buckets |
APPLY operators
CROSS APPLY / OUTER APPLY let a subquery on the right reference columns from a table on the left -- SQL Server's equivalent of PostgreSQL's LATERAL join, commonly used for "top N per group".
SELECT d.department_name, top_emp.first_name, top_emp.salary
FROM departments d
CROSS APPLY (
SELECT TOP 1 first_name, salary
FROM employees e
WHERE e.department_id = d.department_id
ORDER BY salary DESC
) top_emp;
MERGE (upsert)
MERGE INTO inventory AS t
USING (SELECT 'A1' AS sku, 5 AS qty) AS s
ON (t.sku = s.sku)
WHEN MATCHED THEN UPDATE SET t.qty = t.qty + s.qty
WHEN NOT MATCHED THEN INSERT (sku, qty) VALUES (s.sku, s.qty);
TRY...CATCH
SQL Server's structured error handling -- the SQL Server equivalent of PL/SQL's EXCEPTION block.
BEGIN TRY
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
IF @@ROWCOUNT = 0 THROW 50000, 'Account not found', 1;
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE();
ROLLBACK;
END CATCH
Stored procedures & triggers
CREATE PROCEDURE give_raise @emp_id INT, @pct DECIMAL(5,2) AS
BEGIN
UPDATE employees SET salary = salary * (1 + @pct/100) WHERE employee_id = @emp_id;
END;
CREATE TRIGGER trg_salary_audit ON employees
AFTER UPDATE AS
BEGIN
INSERT INTO salary_audit (employee_id, old_salary, new_salary)
SELECT i.employee_id, d.salary, i.salary
FROM inserted i JOIN deleted d ON i.employee_id = d.employee_id;
END;
Index types
| Index | Best for |
|---|---|
| Clustered | defines the physical row order -- one per table |
| Nonclustered | a separate lookup structure pointing back to the table -- many per table |
| Unique | enforces uniqueness, clustered or nonclustered |
| Covering (with INCLUDE) | holds every column a query needs, avoiding a key lookup |
| Columnstore | column-oriented storage tuned for large analytical scans |
| Filtered | indexes only a WHERE-filtered subset of rows |
Transaction isolation levels
| Level | Behaviour |
|---|---|
| READ UNCOMMITTED | allows dirty reads -- reads uncommitted changes from other transactions |
| READ COMMITTED (default) | each statement sees only data committed before it started |
| REPEATABLE READ | prevents non-repeatable reads on rows already read |
| SERIALIZABLE | fully isolated -- also prevents phantom reads |
| SNAPSHOT | each transaction sees a consistent versioned snapshot, no blocking reads |
Handy admin queries
-- currently running queries/sessions
SELECT session_id, status, command, wait_type, blocking_session_id
FROM sys.dm_exec_requests;
-- table size
EXEC sp_spaceused 'employees';
-- kill a stuck session
KILL 55;
Practise what's on this page
Run any query on this page live against a real schema — free, no signup, results in under a second.
Open the SQL editor →Frequently asked questions
Is this SQL Server cheat sheet free to use?
Yes. Every reference table here, the underlying tutorial, and the free SQL editor are completely free with no sign-up.
What's the difference between CROSS APPLY and CROSS JOIN?
CROSS APPLY lets the right-hand subquery reference columns from the left-hand table, which a plain JOIN can't do — that's what makes it suitable for "top N per group" queries. A CROSS JOIN can't reference the other side's columns at all.
What's the difference between a clustered and nonclustered index?
A clustered index determines the actual physical storage order of the table's rows, so a table can have at most one. A nonclustered index is a separate structure that stores the indexed columns plus a pointer back to the row, and a table can have many.
Can I print or save this cheat sheet?
Yes — it's a normal web page, so your browser's print-to-PDF or bookmark feature works fine. There is no separate download required.
Where can I practise T-SQL?
The SQL Server tutorial covers every topic on this page in depth, and the free in-browser SQL editor lets you run queries live.