For a query like this:
select top 40 ColumnNotInIdexCausingPKLookup from table
where ColumnIncludedInIndex=A
order by ColumnIncludedInIndex,ColumnIncludedInIndex,ColumnIncludedInIndex
Any way to push up a sort/filter operator, so the PK lookup isn't that heavy (40 records vs 4.797)? All columns that are being used in the filter/sort are in the index "included column" list so there is enough information in the index to not have 4797 PK lookups
If i change the index so the columns are in de index fields instead of the "include" fields then the execution plan does filter first, using the index itself. However, that only works with that specific set of orderby/filterby. Which changes if the user filters/sorts on another field.
+- 700 reads
SQL Version: Microsoft SQL Server 2017 (RTM-GDR) (KB4583456) – 14.0.2037.2 (X64) Nov 2 2020 19:19:59 Copyright (C) 2017 Microsoft Corporation Developer Edition (64-bit) on Windows 10 Pro 10.0 (Build 18363: )
Steps to reproduce
create database ExecutionPlanTestDB
go
use ExecutionPlanTestDB
create table ExecutionPlanTest (
id int not null identity(1,1) primary key,
ColumnInIndex bigint,
ColumnInIndex_Include bigint,
ColumnNotInIndex bigint,
)
insert into ExecutionPlanTest(ColumnInIndex,ColumnInIndex_Include,ColumnNotInIndex)
select
top 10000000
o1.object_id+o2.object_id+o3.object_id+o4.object_id,
o1.object_id+o2.object_id+o3.object_id+o4.object_id,
o1.object_id+o2.object_id+o3.object_id+o4.object_id
from sys.objects o1
inner join sys.objects o2 on 1=1 and o2.object_id<100
inner join sys.objects o3 on 1=1 and o3.object_id<100
inner join sys.objects o4 on 1=1 and o4.object_id<100
where o1.object_id<100
create index IX_EPT_ColumnInIndex on ExecutionPlanTest(ColumnInIndex) include(ColumnInIndex_Include)
-- execute with execution plan from here
select top 5 ColumnNotInIndex from ExecutionPlanTest
where ColumnInIndex=307 --should have +-2k results
order by ColumnInIndex_Include
Query plan: https://www.brentozar.com/pastetheplan/?id=S1odVbOgu
One way to solve this seems to be changing it to this structure. Although I would rather not convert all the queries like this. It seems to be only logical that sql server first sorts/filters/tops the data in the index itself before sending it further in the execution plan?
select ColumnNotInIdexCausingPKLookup from table where tableID in (
select top 40 tableid from table
where ColumnIncludedInIndex=A
order by ColumnIncludedInIndex,ColumnIncludedInIndex,ColumnIncludedInIndex
)
This results in the better plan (+-700 reads now instead of 20k)
Best Answer
I think you answered your own question, SQL Server will only use an index for sorting if the columns are in the index definition keys (the sorted part) not in the included column list (as then the values are not sorted). If your users can choose one of multiple sorts you will need multiple supporting indexes, and some carefully written queries.