Question

Well, the question is not really "how to avoid" sorting as it is required by business logic, but how to do it preliminary using index, and not on the go.

I have a query plan:

|--Sort(TOP 5, ORDER BY:([this_].[DateAdded] DESC))
   |--Nested Loops(Inner Join, OUTER REFERENCES:([this_].[Id], [Expr1002]) OPTIMIZED WITH UNORDERED PREFETCH)
        |--Index Seek(OBJECT:([storm].[dbo].[Items].[IX_Items_ByLocationSorted] AS [this_]), SEEK:([this_].[Status]=(1) AND [this_].[RegionId]=(32) AND [this_].[LocationId]=(32001)),  WHERE:([storm].[dbo].[Items].[SubcategoryTypeId] as [this_].[SubcategoryTypeId]=(88) AND ([storm].[dbo].[Items].[IsHidden] as [this_].[IsHidden]<(1) OR [storm].[dbo].[Items].[IsHidden] as [this_].[IsHidden]>(1))) ORDERED FORWARD)
        |--Clustered Index Seek(OBJECT:([storm].[dbo].[Items].[PK_Items] AS [this_]), SEEK:([this_].[Id]=[storm].[dbo].[Items].[Id] as [this_].[Id]) LOOKUP ORDERED FORWARD) 

You can see, that Index Seek operation returns a set of rows. Than Query Optimizer does a Key Lookup to retrieve the whole row (since he'd have to return it anyway), and than it sorts all those rows by DateAdded column. It's an obvious and perfectly valid behavior. But it's really slow (might take up to 40 sec), considering the number of rows, returned by Index Seek (30k max).

How can I speed this query up and possibly avoid this on-the-go sorting?

PS: The queried table has about 3 million rows, and is frequently updated. This might cause page locking but I don't think those locks can last 40 seconds.

Query:

SELECT TOP 5 * 
FROM Items 
WHERE 
     SubcategoryTypeId = 88 
     and RegionId = 32
 and LocationId = 32001
 and not (IsHidden = 1 and Status = 1) 
 ORDER BY DateAdded desc
Was it helpful?

Solution

Based on your description and how SQL Server handles indexes, no.

The sort is a necessary step, and there is no "innate" order to SQL Server data. Indexed data is sorted based on the creation script for the index, but it looks like you would have a resort regardless.

As gbn rightly points out, a covering index would help your performance some but you established in the comments that you aren't interested in doing that, so you are probably at a performance plateau that you won't be able to get off of without changing a requirement or your data structure.

OTHER TIPS

You need a covering index to remove the key lookup

This will remove the intermediate sort (ORDERED FORWARD) which is required to match the 2 sets of data (index seek intersected with PK lookup)

Edit:

After comment: maintain a large index (define large?) or live with slow performance. You can't have fast performance with poor indexes. It is a binary choice.

if you'd be willing to make a partial covering index, you should be able to do something like this (note that this code is based off of a table of mine, you'd have to modify it to fit your needs):

    select
    b.*
from
(
    select 
        IDkey,
        row_number() over (order by Years, Months) as z
    from dbo.tblWIN_LOSS
    where Won>=10000
) a
    inner join dbo.tblWIN_LOSS b
        on a.IDkey=b.IDkey
where a.z<=5

i'm not 100% sure if this would give you a performance boost, but i think it should.

Edit: Ahh, you said nhibernate is returning the query, so i don't know what you can do.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top