Query Optimization
Most slow queries share a handful of fixable causes. Learn to spot them and rewrite queries so indexes can do their job.
On this page · 5 min · Advanced
On this page
- In simple words
- Learning objectives
- Explanation
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 5 min
- Difficulty
- Advanced
- Updated
- Oct 27, 2026
Related tutorials
In simple words#
Optimising a query is like packing for a trip: take only what you need (columns), go straight to the right shelf (indexes), and avoid repeated trips (row-by-row loops).
- SARGable
- A filter that can use an index (no function on the column).
- SELECT only needed columns
- Less data to read and send.
- Set-based
- One statement for many rows, not loops.
Learning objectives#
- Write SARGable WHERE clauses
- Avoid implicit conversions
- Prefer set-based operations to loops
- Recognise parameter sniffing
Explanation#
A predicate is SARGable (Search ARGument-able) when SQL Server can use an index seek. Wrapping the column in a function — YEAR(OrderDate) = 2026 — usually prevents it.
Comparing columns to values of a different type causes implicit conversion, which can force scans. Match parameter types to column types.
Return only the columns and rows you need, and process data in sets rather than row-by-row cursors or loops.
Parameter sniffing: a procedure’s plan is optimised for the first parameter values it sees, which may suit later values badly. Options include OPTIMIZE FOR, RECOMPILE for rare cases, or Query Store plan forcing.
Code example: Making a date filter SARGable#
-- ✗ Not SARGable: the function on the column forces a scan
SELECT OrderId FROM dbo.Orders WHERE YEAR(OrderDate) = 2026;
-- ✓ SARGable: compare the bare column with a range
SELECT OrderId FROM dbo.Orders
WHERE OrderDate >= '2026-01-01' AND OrderDate < '2027-01-01';
OrderId 101 102 103 104 -- Same rows; with an index on OrderDate the second query uses an Index Seek.
This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- Both queries return all four 2026 orders.
- The first must compute YEAR() for every row, so it cannot seek.
- The second leaves OrderDate untouched, so an index on OrderDate can seek to the range.
- Using < next year’s date (not <= 31 Dec) also handles time values safely.
Real-world use case#
Rewriting a single non-SARGable report query often cuts its runtime from minutes to milliseconds.
Common mistakes#
WHERE ISNULL(Status, '') = 'Delivered' prevents seeks; WHERE Status = 'Delivered' already excludes NULLs.
Row-by-row loops are often hundreds of times slower than one set-based UPDATE.
Best practices#
- Keep indexed columns bare in WHERE and JOIN
- Match data types between parameters and columns
- Select only needed columns
- Measure with actual plans and logical reads
Remember this#
Don’t wrap indexed columns in functions in WHERE.
Select only needed columns.
Think in sets, not loops.
Practice exercise#
Your task: Rewrite WHERE LEFT(Name, 3) = 'Lap' to be SARGable.
Show solution
WHERE Name LIKE 'Lap%'
Mini quiz#
Which predicate can use an index seek on OrderDate?
Want more? Practise all SQL Server MCQs · SQL Server interview questions
My notes#
Saved in this browser.