DEV Community

LingamSuyambu
LingamSuyambu

Posted on Edited on Originally published at sqlcloudlab.blogspot.com

SARGable Queries in SQL Server: Why Functions in WHERE Slow You Down

Originally published at SQL & Cloud Lab.

This post was drafted with AI assistance.

You add the perfect index, run the query, and SQL Server still scans the whole table. One of the most common reasons is a non-SARGable predicate: a WHERE clause written in a way that stops SQL Server from using the index to seek. The fix is usually a small rewrite.

What SARGable means

SARGable stands for Search ARGument-able. A predicate is SARGable when SQL Server can use it to jump straight to the matching rows in an index (an index seek). If you wrap the indexed column in a function or calculation, SQL Server can no longer search the index for a value; it has to calculate the expression for every row, which means a scan.

The rule of thumb: leave the column bare on one side of the comparison, and do any calculation on the other side.

Common non-SARGable patterns and their fixes

Functions on date columns

-- Not SARGable: YEAR() runs on every row
WHERE YEAR(OrderDate) = 2026

-- SARGable: a range on the bare column
WHERE OrderDate >= '2026-01-01'
  AND OrderDate <  '2027-01-01'
Enter fullscreen mode Exit fullscreen mode

Use a half-open range (>= start and < next start) so it works correctly for date and datetime columns alike.

String functions

-- Not SARGable
WHERE LEFT(EmployeeCode, 3) = 'ABC'
WHERE SUBSTRING(EmployeeCode, 1, 3) = 'ABC'

-- SARGable: a prefix LIKE can seek
WHERE EmployeeCode LIKE 'ABC%'
Enter fullscreen mode Exit fullscreen mode

Note that a leading wildcard, such as LIKE '%ABC', cannot seek, because the index is sorted by the start of the value.

Arithmetic on the column

-- Not SARGable
WHERE Amount * 1.18 > 1000

-- SARGable: move the arithmetic to the constant
WHERE Amount > 1000 / 1.18
Enter fullscreen mode Exit fullscreen mode

ISNULL and COALESCE in filters

-- Not SARGable
WHERE ISNULL(Status, '') = 'Open'

-- SARGable: NULL never equals 'Open', so the function is not needed
WHERE Status = 'Open'

-- When you really need NULLs too
WHERE (Status = @Status OR Status IS NULL)
Enter fullscreen mode Exit fullscreen mode

Implicit conversions

This one is easy to miss because the function is hidden. If a VARCHAR column is compared with an NVARCHAR value, SQL Server may convert the column on every row, which can turn a seek into a scan:

-- EmployeeCode is VARCHAR(20)
WHERE EmployeeCode = N'E1001'  -- NVARCHAR literal: can force a conversion
WHERE EmployeeCode = 'E1001'   -- matching type: clean seek
Enter fullscreen mode Exit fullscreen mode

This happens a lot from application code. In .NET, string parameters default to NVARCHAR; for VARCHAR columns, set the parameter type explicitly (for example SqlDbType.VarChar, or configure the column type in EF Core). The execution plan shows a warning icon for implicit conversions, so it is easy to confirm.

What about OR and NOT?

  • OR across different columns (WHERE A = 1 OR B = 2) often prevents a single seek. Rewriting it as two queries combined with UNION (or UNION ALL when rows cannot overlap) can let each part use its own index.
  • <> and NOT IN are technically SARGable, but they usually match so many rows that a scan is the right choice anyway.

When you cannot change the expression

If the calculation is unavoidable, for example you always filter on an expression, you can add a computed column for it and index that column. SQL Server can then seek on the indexed expression:

ALTER TABLE dbo.Orders
ADD OrderYear AS YEAR(OrderDate);

CREATE INDEX IX_Orders_OrderYear ON dbo.Orders (OrderYear);
Enter fullscreen mode Exit fullscreen mode

Queries that use YEAR(OrderDate) = 2026 or OrderYear = 2026 can then use the new index.

How to check your fix

Run the query with the actual execution plan and SET STATISTICS IO ON. A successful rewrite changes an Index Scan into an Index Seek and drops logical reads sharply, often from thousands to a handful.

Quick rules

  • Keep indexed columns bare in WHERE and JOIN conditions.
  • Replace date functions with ranges, and LEFT/SUBSTRING with prefix LIKE.
  • Move arithmetic to the constant side.
  • Match parameter data types to column data types.
  • Use an indexed computed column when the expression is unavoidable.

More SQL Server guides on my blog, SQL & Cloud Lab.

Top comments (0)

⌂ 换网址