What is driving SQL Server to use less optimal execution plan for queries where 6000+ rows are returned? I need to improve query performance for scenario where all rows are returned.
I select all fields and add rank over same three columns included in index. Depending on number of returned rows, query has two different execution plans, hence execution takes 0.2s or 3s respectively.
From 1 row returned up to ca. 5000 query runs fast. From 6000 rows returned up to all, query runs slow.
Table1 has ca. 38000 rows. Database runs on Azure SQL v12.
Table:
CREATE TABLE [dbo].[Table1](
[ID] [int] IDENTITY(1,1) NOT NULL,
[KOD_ID] [int] NULL,
[SYM] [nvarchar](20) NULL,
[AN] [nvarchar](35) NULL,
[A] [nvarchar](10) NULL,
[B] [nvarchar](2) NULL,
[C] [datetime] NULL,
[D] [datetime] NULL,
CONSTRAINT [PK_Table1] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
GO
CREATE NONCLUSTERED INDEX [IX_Table1] ON [dbo].[Table1]
(
[KOD_ID] ASC,
[SYM] ASC,
[AN] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
Queries:
SELECT TOP 6000 *, RANK() OVER(ORDER BY KOD_ID ASC, SYM ASC, AN ASC) AS Rank#
FROM [dbo].[Table1]
SELECT TOP 7000 *, RANK() OVER(ORDER BY KOD_ID ASC, SYM ASC, AN ASC) AS Rank#
FROM [dbo].[Table1]
Execution plans for both queries

*necessary? This prohibits the use of a covering index, pushing the optimizer into a plan that simply scans the clustered index and sorts the whole table first because it thinks the nonclustered index won't sufficiently reduce I/O (and apparently, as it turns out, sorting takes even more time). Try addingORDER BY KOD_ID ASC, SYM ASC, AN ASCto the query as a whole, this may tip the balance in favor of the index again. - Jeroen Mostert*versus spelling them out makes no difference (performance-wise). - Jeroen MostertSELECT TOP(7000) T.*, T1.Rank# FROM (SELECT TOP(7000) ID, RANK() OVER(ORDER BY KOD_ID ASC, SYM ASC, AN ASC) AS Rank# FROM Table1 ORDER BY ID) T JOIN Table1 ON T.ID = Table1.ID. This is fairly hacky even if it works, though, since it prevents the optimizer from doing a single table scan even where that really is the best approach. Reclustering your table (as presented in the answer) is another promising approach. - Jeroen MostertSELECT ID, RANK() OVER(ORDER BY KOD_ID ASC, SYM ASC, AN ASC) AS Rank# FROM [dbo].[Table1]on ID column. - Megrez7