Step through your stored procedures. See what every line costs.
SpDev is a VS Code extension for SQL Server. Its debugger is free: breakpoints, variables, temp tables and result sets, one statement at a time. SpDev Pro measures every statement and reads the actual execution plans, so you know which line is slow and why.
For SQL Server 2016 and later, and Azure SQL Database. 2,400 installs on the VS Code Marketplace.
-
CREATE PROCEDURE dbo.usp_CustomerStatement -
@CustomerId INT, -
@Status NVARCHAR(20) -
AS -
BEGIN -
SET NOCOUNT ON; -
-
DECLARE @Open INT, @Total DECIMAL(12, 2); -
-
SELECT @Open = COUNT(*)15 ms (65%) · 251 reads · 1 row -
FROM dbo.ProOrders -
WHERE Status = @Status;Implicit conversion of ProOrders.Status prevents an index seek
SQL Server converts the column ProOrders.Status to nvarchar(20) to compare it with a value of a different type, so it cannot seek an index on it. Declare the variable or parameter with exactly the column's type.
Index Scan reads 100,000 rows of dbo.ProOrders to keep 10,000.
-
-
SELECT @Total = SUM(Total)8 ms (35%) · 671 reads · 1 row -
FROM dbo.ProOrders -
WHERE CustomerId = @CustomerId;Missing index on dbo.ProOrders (CustomerId)
SQL Server estimates this index would make the statement 99% cheaper.
CREATE NONCLUSTERED INDEX [IX_ProOrders_CustomerId] ON [dbo].[ProOrders] ([CustomerId]); -
-
SELECT c.Name, c.Country, @Open AS OpenOrders, @Total AS Total0 ms · 2 reads · 1 row -
FROM dbo.ProCustomers AS c -
WHERE c.CustomerId = @CustomerId; -
END
A T-SQL debugger, back in your editor
SQL Server Management Studio dropped its T-SQL debugger after version 17.9. SpDev puts one in VS Code, and it works the way you debug C#.
Free, MIT-licensed, and it never contacts our servers.
- Breakpoints and stepping
- Step over, into and out with F10, F11 and Shift+F11. Step Back goes backwards, one statement at a time.
- Every variable, at every line
- Parameters, variables, table variables, temp tables, table-valued parameters and XML, as they were before each statement.
- Nested procedures
- Step into the procedures you call. SpDev runs your local .sql files, so you can edit and run again without deploying.
- Result sets where they happen
- Hover a SELECT that has run to see the rows it returned. PRINT output goes to the Debug Console.
- Errors on the right line
- SQL Server errors are mapped back to the line in your file, and the debugger stops on errors nothing caught.
- Calls from your app
- Attach to a running .NET app and stop inside the procedures it calls through EF Core, Dapper or ADO.NET.
SpDev Pro shows why a procedure is slow
Click Profile above a procedure. SpDev runs it once and measures the time, CPU, reads, writes and rows of every statement. Then it reads the actual execution plans and puts each problem on the line that causes it, with the fix.
| Finding | What SpDev tells you |
|---|---|
| Missing indexes | Scans that read far more than they keep, such as "reads 200,000 rows of dbo.Orders to keep 39", with the CREATE INDEX to copy. |
| Implicit conversions | The column SQL Server had to convert, and the type your parameter or variable should have so the index can be used. |
| Parameter sniffing | A plan compiled for one value and reused for another: "compiled for @Kind = 'C', ran with 'A'". |
| Key lookups | Lookups that ran thousands of times, and the INCLUDE columns that remove them. |
| Functions on columns | Predicates like YEAR(CreatedAt) = 2024 that can't seek, with the rewrite that can. |
| Bad estimates | Where SQL Server expected 1 row and got 90,000, including the row counts table variables hide. |
| Spills and memory grants | Sorts and hashes that spilled to tempdb, and grants much larger than the query used. |
| Scalar functions | User-defined functions that run once for every row. |
| Row-by-row code | Cursors and WHILE loops whose statements dominate the run. |
| Transactions and waits | Transactions held open across slow statements, and statements that waited on locks or disk. |
How it works, without AI
Every number comes from SQL Server, and every finding comes from a fixed rule. Run the same procedure twice and you get the same findings, each pointing at a value in the plan you can check yourself.
-
Measure each statement
Before each statement, SpDev reads your session's own counters from
sys.dm_exec_requests. The difference between two readings is that statement's cost. SpDev measures its own overhead, a few microseconds, and subtracts it. -
Capture the actual plans
For statements that read tables, SpDev turns on
SET STATISTICS XMLand keeps the plan SQL Server actually ran, with real row counts, warnings and waits. That needs the SHOWPLAN permission; without it you still get the line-by-line costs. -
Check the plans against known problems
SpDev walks each plan and applies rules a performance specialist would apply by eye: rows read against rows kept, converted columns, compiled against runtime parameter values, estimates against actuals.
Also in Pro
- Before and after
- Every profile is kept. Change the code, profile again, and SpDev compares the two runs statement by statement. Logical reads are deterministic, so you can tell whether the fix worked or the server was just quieter.
- Benchmarks
- Run a procedure several times, each run rolled back, and get the median, p95 and spread of every statement.
- Sandbox runs
- Debug, run or profile inside a transaction that is rolled back afterwards, so testing doesn't change your data.
- Coverage
- Statement and branch coverage of your procedures in VS Code's Test Coverage view: which IF branches no test has reached.
- Reports
- Export a profile with its findings and the comparison as Markdown, ready for a ticket or a pull request.
Pricing
Try everything in Pro for 14 days, no card needed: run SpDev: Start Free Pro Trial in VS Code.
Debugger
$0 forever
Anyone, any number of machines
Step through stored procedures and scripts.
- Breakpoints, step into, step out, step back
- Variables, temp tables and table variables at every line
- Result sets, PRINT output and errors on the right line
- Nested procedures from your local files
- Attach to a running .NET app (EF Core, Dapper, ADO.NET)
Pro
$79 per year
or $9 per month
1 developer, on up to 2 machines
Find out why a procedure is slow, and what to change.
- Everything in the debugger
- Line-by-line time, CPU, reads, writes and rows
- Findings from the actual execution plans, with the fix
- Before and after comparison of every run
- Benchmarks, sandbox runs and procedure coverage
Team
$590 per year
$59 per developer
10 developers, 2 machines each
SpDev Pro for a whole team on one licence key.
- Everything in Pro, for 10 developers
- One key; free a seat when someone leaves
- Pay by card, or by invoice and bank transfer
- Offline licences for machines without internet access
Prices in US dollars. Taxes are added at checkout where they apply. Questions about licences, seats and invoices
Your code stays on your machine
SpDev connects only to the database you point it at. Your SQL, your data and your connection strings never leave your computer. Connection strings are kept in VS Code's secret storage.
The free debugger never contacts our server. SpDev Pro sends the licence server your licence key, a hash of VS Code's machine id and the computer's name, so a seat can be counted and freed.
From the blog
Practical notes on stored procedures, execution plans and SQL Server performance. All articles
- 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.
- 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.