Key Takeaways:
- An index scan reads many or all index entries when SQL Server cannot efficiently navigate directly to specific rows.
- Index scans can be efficient when queries return many rows or scanning costs less than repeated seeks and key lookups.
- Non-SARGable predicates, low selectivity, missing indexes, or inaccurate statistics can cause SQL Server to choose an index scan.
- Always evaluate Index Scans using actual execution plans, row estimates, I/O, and query requirements instead of assuming scans are inefficient.
Index Seek vs Scan Performance: Which One Is Faster?
Neither is always faster. An index seek is faster when a query needs a small number of rows. An index scan is faster, and often the better choice, when a query needs a large share of the table. The right operator depends on how many rows qualify, not on the operator’s name.
This is the question behind most searches for index scan vs index seek, and the honest answer is “it depends.” Here is why.
When an index seek is faster
- The query filters on a selective column, so only a few rows qualify.
- The index can go straight to those rows without reading the rest.
- It reads fewer pages, which means less I/O and a smaller memory footprint.
When an index scan is faster
- The query needs a large portion of the rows, so reading everything in order is cheaper than jumping around the index.
- The table is small, so a scan costs only a few pages.
- A narrow, covering nonclustered index can be scanned in place of the much wider clustered index.
- A seek would need thousands of key lookups, and each lookup is a separate random read.
When a seek is not a good sign
An index seek can still be slow. If a seek returns a large number of rows and then does a key lookup for each one, it can cost far more than a single scan. A seek on the inner side of a nested loop that runs millions of times is another common culprit.
Practical takeaway: Don’t treat scan versus seek as good versus bad. Compare the actual rows read, the logical reads, and the elapsed time for each plan.
Why Is SQL Server Using an Index Scan Instead of an Index Seek?
SQL Server uses an index scan instead of an index seek when the Query Optimizer estimates that reading the whole index (or a large part of it) costs less than navigating to specific rows. This usually happens when many rows qualify, when no suitable index exists, or when the query’s filter prevents an index seek.
Index Scan vs Table Scan vs Index Seek
These three operators sound similar, but they read data in different ways. The table below compares them side by side.
What is Index Scan?
An Index Scan occurs when SQL Server reads through an index to find the rows required by a query. Instead of navigating directly to specific values, SQL Server scans a large portion or the entire index. An Index Scan can be efficient when a query returns many rows or when scanning costs less than seeking and performing multiple Key Lookups.
- Clustered Index Scan - for a clustered index
- Index Scan - non-clustered index
- Table Scan - Heap
A Clustered Index Scan is essentially SQL Server scanning the rowstore data of the table through the clustered index structure, because a clustered index contains the actual data of the table at the leaf level.
makes this clear: The leaf level of a clustered index contains the data rows for the table. A nonclustered index has a separate set of leaf level pages.
Consider a simple example:
SELECT *
FROM dbo.Employee;
If Employee has a clustered index, then SQL Server can reasonably choose a Clustered Index Scan, since the query needs all the columns, and all the rows. If you need almost the entire table, there’s not much point in getting it row by row.
Is an Index Scan Always Bad in SQL Server?
No, and this is one of the most important things to understand about execution plans.
Take a table with 10,000 rows. A query with no filter, such as SELECT * FROM dbo.Employee, needs all 10,000 rows, so a scan is entirely reasonable. But a query like:
SELECT *
FROM dbo.Employee
WHERE EmployeeID = 100;is another matter. If EmployeeID is the clustered index key, then SQL Server can go straight to that row, and a seek is probably going to be a lot more efficient.
The general pattern looks like this:
Finally, the optimizer chooses based on estimated cost, not on a strict rule like “seek is better than scan”.
What Is an Index Seek in SQL Server?
An Index Seek occurs when SQL Server navigates an index to find specific rows that match a query condition. It typically processes fewer rows and pages than an Index Scan, making it efficient for selective queries. However, a seek is not always faster because multiple Key Lookups or large result sets can increase its cost.
SELECT
EmployeeID,
FirstName,
LastName
FROM dbo.Employee
WHERE EmployeeID = 617005; If the table has:
CREATE CLUSTERED INDEX CIX_Employee_EmployeeID
ON dbo.Employee(EmployeeID); Instead of reading leaf pages as it goes, SQL Server can go straight down the index’s B-tree structure to the key it needs. A seek can be efficient for selective predicates for this reason, it goes directly to the part of the index that is needed rather than scanning all of it.
A Seek Doesn't Always Mean "Only Qualifying Rows Are Read"
"An Index Seek only touches rows that qualify" is the usual explanation, but it's an oversimplification. What a seek really does is find an appropriate start point or range in the index SQL Server may still have to read a significant number of rows or pages when it gets there.
For example:
SELECT *
FROM dbo.Employee
WHERE EmployeeID BETWEEN 100000 AND 500000; This is still a seek, but the range is large, so the seek might touch a significant number of index pages. Here is what the process looks like:
Index Seek → Find starting position → Read required range → Apply predicates → Return qualifying rows
To summarize, there can be a big difference between the number of rows returned and the number of rows or pages touched, and this difference matters when estimating the actual cost of a plan.
Types of Rowstore Indexes
For traditional disk-based rowstore tables, SQL Server supports two primary index types:
- Clustered Index
- Nonclustered Index
SQL Server also offers other specialized index technologies, including columnstore, filtered, XML, spatial, and memory-optimized indexes, as well as vector indexes introduced in SQL Server 2025. This article focuses specifically on clustered and nonclustered rowstore indexes.
Clustered Index
A clustered index determines the physical storage order of rows in a table. Its leaf level is the actual data not a separate copy of it. If a clustered index is built on EmployeeID, the table's rows are physically organized by that key, and Microsoft's current documentation confirms this: the clustered index stores the table's data directly at the leaf level.
Heaps: Tables Without a Clustered Index
A table with no clustered index is called a heap. Its rows are stored as unordered heap data pages, and SQL Server accesses them with a Table Scan when a full read is required.
Primary Key Is Not Always a Clustered Index
SQL Server will automatically create a clustered index for a primary key if the table doesn't already have one but that's a default behavior, not a hard rule. It's entirely possible to define a nonclustered primary key while placing the clustered index on a different column.
As Microsoft's documentation notes, if neither CLUSTERED nor NONCLUSTERED is specified for a primary key constraint, SQL Server defaults to clustered only if a clustered index doesn't already exist on the table.
Nonclustered Index
A nonclustered index is a separate B-tree structure, distinct from the table's clustered data. It stores the indexed column(s) alongside a row locator that points back to the actual data but what that locator looks like depends on the underlying table structure.
On a heap: the nonclustered index uses a Row Identifier (RID) to locate the row directly within the heap.
On a clustered table: the nonclustered index uses the clustered index key as its row locator, rather than a physical address. This is an important distinction from the older, oversimplified explanation that a nonclustered index simply "stores the address of the row." Because clustered key columns are carried into every nonclustered index as part of the row locator, a wide clustered key can meaningfully increase the size of all nonclustered indexes on that table , a real consideration in index design.
Covering Indexes and Included Columns
A nonclustered index can include additional, non-key columns to satisfy a query entirely from the index itself. For example:
CREATE NONCLUSTERED INDEX IX_Employee_City
ON dbo.Employee(City)
INCLUDE (EmployeeName, Gender, DOB); Given this index, the query:
SELECT
EmployeeName,
Gender,
DOB
FROM dbo.Employee
WHERE City = 'Kochi'; can be satisfied entirely by the nonclustered index, since it already contains every column the query needs. SQL Server can perform an Index Seek without ever touching the base table ,this is known as a covering index.
Key Lookups
Now consider a query that needs columns the nonclustered index doesn't cover:
SELECT
EmployeeID,
EmployeeName,
Gender,
DOB,
City
FROM dbo.Employee
WHERE EmployeeName = 'Employee_456'; The nonclustered index can seek to the matching rows, but since it doesn't contain every requested column, SQL Server must retrieve the remaining data from the clustered index:
Nonclustered Index Seek → Key Lookup → Clustered Index → Data Row
This is a critical point: an Index Seek followed by many Key Lookups can sometimes be more expensive than a single Clustered Index Scan. An execution plan should never be judged purely on the presence of the word "Seek" , the number of rows the operators actually process matters just as much.
Scan and Seek Side by Side
Clustered Index Scan -
SELECT * FROM dbo.Employeerequests every column and row, so a full scan is a reasonable choice; SQL Server needs the entire table regardless.
Clustered Index Seek -
SELECT EmployeeID, EmployeeName, City FROM dbo.Employee WHERE EmployeeID = 3can navigate directly to the matching row when EmployeeID is the clustered key, making this a highly selective, efficient operation.
Index Seek with a Range ,a seek doesn't require a single-row match.A query like:
SELECT *
FROM dbo.Employee
WHERE EmployeeID BETWEEN 1000 AND 5000; still qualifies as a Clustered Index Seek, since SQL Server navigates to the start of the range and reads only the relevant portion of the index , it just isn't a single-row seek.
Seek Predicate vs. Residual Predicate
A single query can combine a seekable condition with one that isn't. Consider:
SELECT *
FROM dbo.Employee
WHERE EmployeeID BETWEEN 1000 AND 5000
AND City = 'Kochi'; With a clustered index on EmployeeID, SQL Server can use EmployeeID BETWEEN 1000 AND 5000 as the seek predicate to narrow down the range. But City = 'Kochi' isn't part of that index, so it becomes a residual predicate evaluated row by row within the range the seek already narrowed down.
Seek Predicate → Find relevant range
Residual Predicate → Filter rows within that range
Non-SARGable Predicates
An expected seek can silently turn into a scan when a predicate isn't SARGable (Search ARGument-able) — meaning SQL Server can't use the index to search directly for the value.
Given the index:
CREATE INDEX IX_Employee_DOB
ON dbo.Employee(DOB); a direct comparison like WHERE DOB = '1995-01-10' can use the index efficiently. But wrapping the column in a function breaks that:
SELECT *
FROM dbo.Employee
WHERE YEAR(DOB) = 1995; SQL Server can't seek to a specific value of YEAR(DOB), because the index is built on DOB itself, not on the result of the function. A SARGable rewrite solves the problem by expressing the condition as a range on the actual indexed column:
SELECT *
FROM dbo.Employee
WHERE DOB >= '1995-01-01'
AND DOB < '1996-01-01'; Clustered Index Scan vs. Table Scan
Both of these are used to read through a data structure sequentially, but for different types of storage:
- Heap → Table Scan
- Clustered rowstore table → Clustered Index Scan
How SQL Server Determines Scan vs Seek
- Seeks favor selective queries. The optimizer typically uses the most selective available index when it estimates that only a small percentage of rows qualify.
- Scans cause wide searches. If a query hits a large portion of the table or there is no good index, then the optimizer will resort to a scan. A query without a WHERE clause for example will almost always result in a full scan. You have to read every row anyway.
- Statistics are used to estimate costs. The optimizer decisions are based on estimated I/O and CPU cost which are dependent on accurate statistics. Stale or missing stats can mislead the optimizer. For example, stale stats on a key column can cause it to over estimate selectivity and choose a scan when a seek would have done better. Keeping statistics up to date is one of the easiest things you can do to help the optimizer choose the right access path.
- The scan might stop early. A full scan doesn't necessarily read the whole table. For example the query SELECT TOP 1, or the plan with a predicate that filters out most of the rows at the beginning of the scan, allows SQL Server to stop the scan as soon as enough rows are found.
Performance Considerations
- Cost of I/O and CPU. When a small result set is required, scans read more pages and generally use more I/O and CPU than seeks, since scan cost is proportional to table size. Seek cost, however, scales with the number of rows that qualify and index pages touched. That said, if a query really does need most of the table, a single scan can beat a lot of individual seeks with scattered random I/O.
- Simultaneous execution. Recent versions of SQL Server are more aggressive in parallelizing large scans. For example, SQL Server 2022's Buffer Pool Parallel Scan can utilize multiple CPU cores to scan memory pages on large servers, resulting in 10-30x speedups for operations such as backups and startup scans.
- Index covering. If an index has all the columns that a query needs, a seek can completely avoid the overhead of a key lookup. If the index is not fully covered, the nonclustered index seek might have to reach back to the base table for the missing columns.
- Locks. It can escalate locks and increase contention when scanning large tables. SQL Server 2025 brought optimized locking to reduce lock contention and memory usage for large scans and updates.
- Analytical workloads. For reporting and analytics workloads that need to read large volumes of data, scans can actually be the better choice - a sequential scan preserves the index order (useful for sorting) and can outperform a large number of individual seeks on broad range queries.
New Features in SQL Server 2022 and 2025
- Buffer Pool Parallel Scan (SQL Server 2022+) - Automatically parallelizes buffer-pool scans on machines with large memory, speeding up operations such as backup, restore, and availability-group failover.
- Better index creation - SQL Server 2022 adds WAIT_AT_LOW_PRIORITY for online index builds, which reduces blocking by waiting for lower priority locks. Resumable online index rebuilds (new in SQL Server 2019): You can pause and resume large index operations without having to restart them.
- Auto-drop statistics - The new AUTO_DROP option in SQL Server 2022 will automatically drop statistics that the optimizer no longer uses, reducing unnecessary overhead.
- Columnstore enhancements - SQL Server 2025 delivers ordered nonclustered columnstore indexes and improved rowgroup elimination, which makes full columnstore scans more efficient for analytics queries.
- Vector and AI indexing (SQL Server 2025) - a new VECTOR data type and CREATE VECTOR INDEX statement allow for approximate nearest-neighbor search for AI workloads. If you use VECTOR_SEARCH without a vector index, SQL Server does a brute-force scan over all vectors. If you use an ANN (approximate nearest-neighbor) vector index, SQL Server can jump directly to similar embeddings, which greatly speeds up similarity searches.
- Intelligent Query Processing - incremental improvements such as plan parameterization feedback and cardinality feedback on expressions allow the optimizer to improve its cost estimates over time, leading to better seek-versus-scan decisions.
Conclusion
Neither an Index Seek nor an Index Scan is inherently good or bad. SQL Server chooses between them based on factors such as selectivity, table size, available indexes, statistics, and the amount of data the query needs to process.
An Index Seek can still touch many rows when the requested range is large, and multiple Key Lookups can sometimes cost more than a single Clustered Index Scan. Similarly, non-SARGable predicates can prevent SQL Server from efficiently using an index and lead to an Index Scan.
The goal is not to eliminate scans, but to help SQL Server choose the most efficient access method for each query. Keeping indexes appropriate and statistics accurate gives the optimizer better information when selecting between an Index Seek, Index Scan, or other access methods.
When troubleshooting performance, always examine the actual execution plan, estimated versus actual rows, I/O, Key Lookups, and predicate conditions rather than assuming that an Index Seek is automatically faster than an Index Scan.
FAQ
1. Is an Index Seek always faster than an Index Scan?
No. An index seek is not always faster than an index scan. SQL Server chooses the lower-cost access method based on selectivity, rows processed, I/O, statistics, and available indexes. A seek with many key lookups can sometimes cost more than a scan.
2. Why is SQL Server using an Index Scan instead of an Index Seek?
SQL Server may choose an Index Scan instead of an Index Seek when a query returns many rows, the predicate has low selectivity, no suitable index exists, statistics are inaccurate, or the optimizer estimates that scanning the index requires less cost than seeking and retrieving individual rows.
3. Can an Index Seek be slower than an Index Scan?
Yes. An Index Seek can be slower than an Index Scan when it touches a large range of rows or performs many Key Lookups. SQL Server compares estimated I/O and CPU costs, so a scan can be more efficient when the query needs substantial amounts of data.
4. What is the difference between an Index Seek and a Clustered Index Seek?
An Index Seek navigates an index to locate a specific value or range. A Clustered Index Seek performs that operation against the clustered index, whose leaf level contains the table's data. Both can efficiently locate selective data without scanning the entire structure.
5. What is the difference between a Table Scan and an Index Scan?
A Table Scan reads data from a heap, meaning the table has no clustered index. An Index Scan reads through a nonclustered index, while a Clustered Index Scan reads the clustered index's leaf-level data. The access method depends on the table structure.
6. What is the difference between Index Seek, Index Scan, and Key Lookup?
An Index Seek navigates an index to find relevant rows. An Index Scan reads through an index or range. A Key Lookup retrieves additional columns from the clustered index after a nonclustered Index Seek when the index doesn't cover the query.
7. Which is faster: Index Scan or Index Seek?
Neither is inherently faster. An Index Seek is often efficient for selective queries that return few rows, while an Index Scan can be efficient when a query needs many rows. SQL Server chooses based on estimated cost, I/O, CPU, statistics, and available indexes.
Further reading:
If you found this article helpful, you might want to check out these related resources,


.avif)