I have the following table:
CREATE TABLE [dbo].[MP_Notification_Audit](
[id] [bigint] IDENTITY(1,1) NOT NULL,
[type] [int] NOT NULL,
[source_user_id] [bigint] NOT NULL,
[target_user_id] [bigint] NOT NULL,
[discussion_id] [bigint] NULL,
[discussion_comment_id] [bigint] NULL,
[discussion_media_id] [bigint] NULL,
[patient_id] [bigint] NULL,
[task_id] [bigint] NULL,
[date_created] [datetimeoffset](7) NOT NULL,
[clicked] [bit] NULL,
[date_clicked] [datetimeoffset](7) NULL,
[title] [nvarchar](max) NULL,
[body] [nvarchar](max) NULL,
CONSTRAINT [PK_MP_Notification_Audit] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[MP_Notification_Audit] ADD CONSTRAINT [DF_MP_Notification_Audit_date_created] DEFAULT (sysdatetimeoffset()) FOR [date_created]
GO
CREATE NONCLUSTERED INDEX [IX_MP_Notification_Audit_TargetUserDateCreated] ON [dbo].[MP_Notification_Audit]
(
[target_user_id] ASC,
[date_created] 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, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
There are over 10000 rows in the table with a [target_user_id]
of 100017
.
When I execute the following query:
SELECT
[target_user_id], [patient_id]
FROM
[dbo].[MP_Notification_Audit]
WHERE
[target_user_id] = 100017
ORDER BY
[date_created] ASC
OFFSET 9200 ROWS
FETCH NEXT 10 ROWS ONLY
... I get the following actual execution plan:
Why did SQL Server need to do 9210 instead of 10 clustered key lookups? The index [IX_MP_Notification_Audit_TargetUserDateCreated]
should've allowed it to figure out the 10 RIDs it needed to retrieve to get [patient_id]
, and only do 10 clustered key lookups, right?
I've also discovered some more odd behaviour - it looks like SQL Server 'punishes' you for not selecting unindexable columns. If I instead OFFSET
10000 rows, I get the following execution plan:
SELECT
[target_user_id], [patient_id]
FROM
[dbo].[MP_Notification_Audit]
WHERE
[target_user_id] = 100017
ORDER BY
[date_created] ASC
OFFSET 10000 ROWS
FETCH NEXT 10 ROWS ONLY
... with the recommendation of creating an index that includes [patient_id]
, and an inefficient clustered index scan for the whole table. The time taken was 0.126s, however this could apparently have been a lot better because when I add the unindexable column [title]
to the query, I get this:
SELECT
[target_user_id], [patient_id], [title]
FROM
[dbo].[MP_Notification_Audit]
WHERE
[target_user_id] = 100017
ORDER BY
[date_created] ASC
OFFSET 10000 ROWS
FETCH NEXT 10 ROWS ONLY
... and the nonclustered index is still used, the time taken only being 0.032s. Does SQL Server basically say "you could've created an index to do this more efficiently, so we're not even going to use the index you do have and we're going to do the lookup inefficiently to punish you", or am I missing something?