The SpDev blog
Practical articles on SQL Server stored procedures, execution plans and performance, written for developers who work in T-SQL every day. Every example runs on SQL Server 2022.
Stored procedures
- How to debug a stored procedure in SQL Server
SSMS no longer has a T-SQL debugger. Here are the ways to debug a SQL Server stored procedure today: Visual Studio, RAISERROR WITH NOWAIT, logging that survives a rollback, and stepping through it in VS Code.
- How to execute a stored procedure in SQL Server: every way, with examples
EXEC with named and positional parameters, defaults, OUTPUT parameters, return codes, INSERT...EXEC, WITH RESULT SETS, calling one procedure from another, and the errors you'll meet.
- How to find text in stored procedures in SQL Server
Search the code of every stored procedure, function, view and trigger for a table, column or phrase. Why INFORMATION_SCHEMA misses matches, how to search all databases, and how to find real dependencies.
- How to write a stored procedure in SQL Server: a template that holds up
A stored procedure template for SQL Server with SET options, parameter checks, a transaction and error handling that works, and the reasons behind each line.
- Stored procedure output parameters, return values and result sets
Three ways a SQL Server stored procedure returns data: result sets, OUTPUT parameters and the return code. How each works, their traps, and which to use when.
- Stored procedure vs function in SQL Server: differences and when to use each
What procedures can do that functions can't, and the other way round. Scalar, inline and multi-statement functions compared, with the performance traps of each, measured.
- Stored procedure vs view in SQL Server: which one to use
Views are saved queries you can select from; stored procedures are programs you execute. How they differ in parameters, performance, security and updates, with examples.
- What is a stored procedure in SQL Server? A practical introduction
What a stored procedure is, how to create and run one, what it's good for and where it hurts, with examples you can run on SQL Server.
Performance
- How to read a SQL Server execution plan
Estimated vs actual plans, how to get them in SSMS, VS Code and T-SQL, how to read the operators, and the eight things to look for when a query is slow.
- Implicit conversion in SQL Server: why your index isn't used
An NVARCHAR parameter against a VARCHAR column can turn a 6-read seek into a 600-read scan. How to spot implicit conversions in the plan, and how to fix them in T-SQL and .NET.
- Monitoring SQL Server performance with built-in tools
Monitor SQL Server performance without buying anything: what's running now, who is blocking whom, performance counters, Query Store regressions and Extended Events, with queries you can run today.
- Parameter sniffing in SQL Server: what it is and how to fix it
Why a stored procedure is fast for one customer and slow for another, how to prove it's parameter sniffing, and the fixes that work, from a covering index to OPTION (RECOMPILE).
- SQL query optimization in SQL Server: a practical checklist
Ten checks that fix most slow SQL Server queries, from sargable predicates to catch-all searches, each with logical reads measured before and after.
- SQL Server indexes explained: clustered, nonclustered and covering
How SQL Server indexes work, when the optimizer uses them and when it ignores them, key lookups, covering indexes and column order, with logical reads measured on real data.
- SQL Server performance tuning: find the slow query before you fix it
A method for SQL Server performance tuning: start from waits, find the queries that cost the most with Query Store and DMVs, fix them one at a time, and prove each fix.
- WITH (NOLOCK) in SQL Server: what it really does, and what to use instead
NOLOCK doesn't just skip waiting: it reads uncommitted data and can miss or double-count rows. A two-session demonstration, the errors it causes, and why READ_COMMITTED_SNAPSHOT is usually the better fix.
T-SQL
- COALESCE vs ISNULL in SQL Server: the differences that matter
ISNULL and COALESCE both replace NULLs, but differ in return type, truncation, nullability and how often they evaluate a subquery. Each difference shown with a query you can run.
- CROSS APPLY and OUTER APPLY in SQL Server, with practical examples
What APPLY does that a JOIN can't: top N rows per group, calling table-valued functions per row, unpivoting columns and reusing calculations. With reads compared against ROW_NUMBER.
- CTEs in SQL Server: WITH, recursive queries and the performance myth
What a common table expression is, how to write recursive CTEs for hierarchies and date series, MAXRECURSION, and why a CTE used twice is computed twice.
- Cursors in SQL Server: when they're fine, and how to replace them
How T-SQL cursors and WHILE loops work, why row-by-row code is slow, the cursor options that matter, and set-based rewrites for the common cases, with timings.
- Error handling in SQL Server: TRY...CATCH, THROW and XACT_STATE
How TRY...CATCH works in T-SQL, the errors it can't catch, THROW vs RAISERROR, doomed transactions and XACT_STATE, and a CATCH block you can copy.
- PIVOT and UNPIVOT in SQL Server, including dynamic PIVOT
Turn rows into columns with PIVOT, the conditional aggregation alternative that's often clearer, a dynamic PIVOT for unknown columns, and UNPIVOT back again.
- SQL Server window functions: ROW_NUMBER, RANK, LAG, running totals and the RANGE trap
Window functions in SQL Server with practical examples: numbering, ranking, top N per group, previous and next rows, running totals, and why the default frame gives different answers and runs slower.
- Temp table vs table variable in SQL Server: which one, and why
Statistics, row estimates, rollback behavior, indexes, recompiles and scope: how #temp tables and @table variables differ in SQL Server 2019 and later, measured on real data.
- The MERGE statement in SQL Server: syntax, pitfalls and safer alternatives
How to write an upsert with MERGE in SQL Server, the errors you'll meet, why it needs HOLDLOCK under concurrency, and when separate UPDATE and INSERT statements are the better choice.
- Transactions in SQL Server: BEGIN, COMMIT, ROLLBACK and XACT_ABORT
How SQL Server transactions really behave: why a failed statement doesn't roll back by itself, what XACT_ABORT changes, how nested transactions work, and the error 266 you get from procedures.
.NET
- AddWithValue, Dapper and EF Core: the NVARCHAR parameter that slows your queries
What parameter types ADO.NET, Dapper and EF Core really send for .NET strings, why NVARCHAR against a VARCHAR column scans the index, and how to fix it in each library. Verified with SQL Server's plan cache.
- Calling SQL Server stored procedures from C#: ADO.NET, Dapper and EF Core
Working C# code to call a stored procedure, read several result sets, and get output parameters and return values with ADO.NET, Dapper and EF Core, plus the mistakes that return null.
Tools
- SQL Server development in VS Code after Azure Data Studio
Azure Data Studio was retired on 28 February 2026. How to set up VS Code for SQL Server work: the MSSQL extension, a local SQL Server in Docker, procedures in source control, debugging and profiling.
- SQL Server Profiler: what it does, why it's deprecated, and what to use instead
SQL Server Profiler is deprecated. How to capture the same trace with Extended Events in T-SQL, read the results with a query, and profile a single stored procedure line by line.