Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ORDER BY indexed column is still slow

I have the following query that takes < 1s when ORDER BY b.Price is used, and over 10s when ORDER BY b.Price DESC is used

select * from
(
    select  
        /* When changed to ORDER BY b.Price DESC it's 10x slower! */
        (row_number() over (ORDER BY b.Price)) as RowNumber,
        b.*     
    from
        Books b (nolock) 
        inner join BookPublishRegions p (nolock)
          on b.BookKey = bp.BookKey
    where       
        contains(p.PublishRegionName, 'France')
) as t1
where t1.RowNumber between 100 and 110

Any thoughts on why?

I have both an ascending and descending index on b.Price. I'm not really sure what else I can do here...

For reference, I'm including the CREATE script for both indexes below:

CREATE NONCLUSTERED INDEX [IX_Books_PriceDesc] ON [dbo].[Books] 
(
    [Price] DESC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]


CREATE NONCLUSTERED INDEX [IX_Books_Price] ON [dbo].[Books] 
(
    [Price] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
like image 999
Ricky Avatar asked Jan 27 '15 18:01

Ricky


People also ask

Do indexes speed up ORDER BY?

Yes, index will help you, when using ORDER BY. Because INDEX is a sorted data structure, so the request will be executed faster.

Should ORDER BY columns be indexed?

When you include an ORDER BY clause in a SELECT statement, you can improve the performance of the query by creating an index on the column or columns that the ORDER BY clause specifies. The database server uses the index that you placed on the ORDER BY columns to sort the query results in the most efficient manner.

Why does index query slow down?

Having two identical indexes makes a negative impact on the performance of SQL queries. It is actually a waste of disk space and also slows down the insertions to the table. Therefore, it is a good practice to avoid duplicate indexes to eliminate these issues. Duplication of indexes can happen in multiple ways.

How can I make my index faster?

Go to Control Panel | Indexing Options to monitor the indexing. The DisableBackOff = 1 option makes the indexing go faster than the default value. You can continue to work on the computer but indexing will continue in the background and is less likely to pause when other programs are running.


1 Answers

As another user has mentioned, it is all wild speculation without seeing a query plan. But I would be surprised if the query used either index in either case. Even if they were covering indexes, you are filtering on the result of a window function in a sub query, the planner has no way of knowing for which rows the row_number function will return 100-110 until it has parsed the entire result set in the sub query, and you haven't actually ordered the subquery by price either, so there'd be no benefit in it using either index. I can't explain why it is faster in the ascending case in these conditions though, we'd have to see a query plan to figure that out, but I suspect something else might be at play.

It looks as if you are doing the window function to implement paging, if so, and you are using 2012 or higher, try using offset/fetch instead, eg:

select  
    b.*     
from
    Books b (nolock) 
    inner join BookPublishRegions p (nolock)
      on b.BookKey = bp.BookKey
where       
    contains(p.PublishRegionName, 'France')
order by price desc 
offset 100 fetch 10

The planner might realise it can use the index then. Although it would probably need to be a clustered or covering index to make any difference to be honest.

If you're on 2008 or earlier, try putting an explicit order by and top in the subquery so the planner realises it can use the index. You can still use the window function and filter in an outer query to do the paging, but this way it will hopefully run the window function over far fewer rows:

select * from
(
select top 110
    (row_number() over (ORDER BY b.Price DESC)) as RowNumber,
    b.*     
from
    Books b (nolock) 
    inner join BookPublishRegions p (nolock)
      on b.BookKey = bp.BookKey
where       
    contains(p.PublishRegionName, 'France')
ORDER BY b.Price DESC
) as t1
where t1.RowNumber between 100 and 110
like image 50
EvilPuppetMaster Avatar answered Oct 10 '22 07:10

EvilPuppetMaster