Execution plans look like a forest of arrows and percentages. They're actually a straightforward map: each box is an operation the database performs, and the arrows show data flowing between them. Once you know what to look for, you can diagnose 80% of slow queries without involving a DBA. Here's the field guide we use ourselves.

How to get the plan

In SSMS or Azure Data Studio, hit Ctrl+M before running the query to include the actual execution plan, or Ctrl+L for the estimated plan. Always prefer the actual plan when you can — estimated plans use statistics, actuals use what really happened.

-- Or with T-SQL, no GUI required:
SET STATISTICS XML ON;
SET STATISTICS IO ON;

SELECT TOP 10 * FROM Orders WHERE CustomerId = 42;

SET STATISTICS XML OFF;
SET STATISTICS IO OFF;

Read it right to left, top to bottom

Plans flow from right to left: the rightmost operators read data, intermediate operators transform it, and the leftmost operator returns the result to you. Arrow thickness shows row count — fat arrows mean lots of data is moving.

The operators that matter

Scan vs. Seek

Joins

Sorts and spools

A Sort operator is a yellow flag — it means the engine had to order rows in memory (or worse, in tempdb). Often eliminated by adding an index on the ORDER BY column. Table Spools mean intermediate results are being written to tempdb. Worth investigating.

The two warnings to never ignore

Implicit conversion

You'll see a yellow exclamation mark on an operator. Hover, and you'll find:

Type conversion in expression
(CONVERT_IMPLICIT(nvarchar(50), [Orders].[CustomerCode], 0))
may affect "CardinalityEstimate" in query plan choice.

This usually means a parameter type doesn't match the column type — for example, a varchar column compared to an NVARCHAR parameter. The conversion happens per-row and disables index usage. Fix the type mismatch and throughput often jumps 10x.

Estimated vs. actual rows mismatch

If an operator estimated 10 rows but actually returned 1,000,000, the optimizer made bad choices downstream — wrong join algorithm, wrong memory grant, possibly a spill. Causes: stale statistics (run UPDATE STATISTICS), parameter sniffing, or a non-SARGable predicate.

SARGability: the one term worth learning

A predicate is "SARGable" (Search ARGument-able) if SQL Server can use an index to evaluate it. Wrap a column in a function and you destroy SARGability:

-- Bad: function on column. Forces a scan.
WHERE YEAR(OrderDate) = 2025;

-- Good: function on the constant side. Index seek.
WHERE OrderDate >= '2025-01-01'
  AND OrderDate <  '2026-01-01';

Same result; very different plan.

When to add an index

If a query is slow and the plan shows a scan or a costly key lookup, an index is often the answer. SQL Server even suggests them — there's a green "Missing Index" hint at the top of the plan when one would help. Don't blindly create it. Indexes cost write performance and storage. Rules of thumb:

-- A typical "covering" index for a hot query.
CREATE NONCLUSTERED INDEX IX_Orders_Customer_Date
ON dbo.Orders (CustomerId, OrderDate)
INCLUDE (Total, Status);

Parameter sniffing in three sentences

The optimizer compiles a plan based on the first parameter values it sees, then caches it. If those values are unrepresentative, every subsequent execution gets a bad plan. Mitigate with OPTION (RECOMPILE) for occasional skewed queries, or with the OPTIMIZE FOR hint for known-bad cases.

Tool tip. SentryOne Plan Explorer (free) is a much nicer way to read big plans than SSMS. It highlights expensive operators, shows accurate cost percentages, and prints warnings prominently.

A simple workflow

  1. Reproduce the slow query in SSMS with actual plan on.
  2. Find the fattest arrow and the operator producing it.
  3. Check for warnings (yellow triangles).
  4. Look at the worst row-estimate mismatch.
  5. Decide: index, query rewrite, or statistics update.
  6. Re-run, compare.

Do that ten times and execution plans stop being scary. They become the most useful diagnostic tool the database gives you.

Our database performance track covers indexing strategy, query rewriting, and tempdb pressure in production workloads. If you've got a particular query that's stubborn, bring it to a coaching session.