Short version: seek is much better
Less short version: seek is generally much better, but a great many seeks (caused by bad query design with nasty correlated sub-queries for instance, or because you are making many queries in a cursor operation or other loop) can be worse than a scan, especially if your query may end up returning data from most of the rows in the affected table.
It helps to cover the whole family for data finding operations to fully understand the performance implications.
Table Scans: With no indexes at all that are relevant to your query the planner is forced to use a table scan meaning that every row is looked at. This can result in every page relating to the table's data being read from disk which is often the worst case. Note that for some queries it will use a table scan even when a useful index is present - this is usually because the data in the table is so small that it is more hassle to traverse the indexes (if this is the case you would expect the plan to change as the data grows, assuming the selectivity measure of the index is good).
Index Scans with Row Lookups: With no index that can be directly used for a seek is found but an index containing the right columns is present an index scan may be used. For instance if you have a large table with 20 columns an index on column1,col2,col3 and use issue SELECT col4 FROM exampletable WHERE col2=616
, in this case scanning the index to query col2
than to scan the whole table. Once matching rows are found then the data pages need to be read to pickup col4 for output (or further joining) which is what the "bookmark lookup" stage is when you see it in query plans.
Index Scans without Row Lookups: If the above example was SELECT col1, col2, col3 FROM exampletable WHERE col2=616
then the extra effort to read data pages is not needed: once index rows matching col2=616
are found all the requested data is known. This is why you sometimes see columns that will never be searched on, but are likely to be requested for output, added to the end of indexes - it can save row lookups. When adding columns to an index for this reason and this reason only, add them with the INCLUDE
clause to tell the engine that it doesn't need to optimise index layout for querying based on these columns (this can speed up updates made to those columns). Index scans can result from queries with no filtering clauses too: SELECT col2 FROM exampletable
will scan this example index instead of the table pages.
Index Seeks (with or without row lookups): In a seak not all of the index is considered. For the query SELECT * FROM exampletable WHERE c1 BETWEEN 1234 AND 4567
the query engine can find the first row that will match by doing a tree-based search on the index on c1
then it can navigate the index in order until it gets to the end of the range (this is the same with a query for c1=1234
as there could be many rows matching the condition even for an =
operation). This means that only relevant index pages (plus a few needed for the initial search) need to be read instead of every page in the index (or table).
Clustered Indexes: With a clustered index the table data is stored in the leaf nodes of that index instead of being in a separate heap structure. This means that there will never need to be any extra row lookups after finding rows using that index no matter what columns are needed [unless you have off-page data like TEXT
columns or VARCHAR(MAX)
columns containing long data]. You can only have one clustered index for this reason, so if you use one chose where you put it carefully in order to get maximum gain.