mssql row_number() over dynamic order by performance issues

Murugan Andezuthu Dharmaratnam 1 Reputation point
2021-01-25T17:54:15.747+00:00

Using case statement in ROW_NUMBER() OVER ( ORDER BY ) causes severe performance issues

I have two queries the first one runs in 5 seconds, the second one takes a couple of minutes to run. I am trying to sort using a value passed to a stored procedure. Kindly let me know how I can fix this issue.

ONE

select top(10) ROW_NUMBER() OVER (
ORDER BY POData.PurchaseOrderNumber
) AS ROW_NUM,
Received.QuantityReceived,POData.PurchaseOrderNumber,POData.*
from POData inner join Received on POData.Id = Received.PODataId

TWO

declare @OrderBy nvarchar(32) = 'PurchaseOrderNumber'
select top(10)
ROW_NUMBER() OVER (
ORDER BY
case when @OrderBy = 'PurchaseOrderNumber' then POData.PurchaseOrderNumber end
) AS ROW_NUM,
Received.QuantityReceived,POData.*
from POData inner join Received on POData.Id = Received.PODataId

SQL Server
SQL Server
A family of Microsoft relational database management and analysis systems for e-commerce, line-of-business, and data warehousing solutions.
14,155 questions
Transact-SQL
Transact-SQL
A Microsoft extension to the ANSI SQL language that includes procedural programming, local variables, and various support functions.
4,665 questions
{count} votes

4 answers

Sort by: Most helpful
  1. Tom Phillips 17,741 Reputation points
    2021-01-25T20:09:45.03+00:00

    Try this:

    declare @OrderBy nvarchar(32) = 'PurchaseOrderNumber'
    
    select top(10)
    *,
    ROW_NUMBER() OVER (
    ORDER BY OrderBy
    ) AS ROW_NUM
    FROM (
    SELECT Received.QuantityReceived,POData.*, OrderBy = case when @OrderBy = 'PurchaseOrderNumber' then POData.PurchaseOrderNumber end
    from POData inner join Received on POData.Id = Received.PODataId
    ) a
    
    0 comments No comments

  2. Erland Sommarskog 113.6K Reputation points MVP
    2021-01-25T22:34:47.693+00:00

    I would try adding this hint at the end:

    OPTION (RECOMPILE)
    

    This permits SQL Server to handle the variable as a constant, and select the best plan for the given value.

    0 comments No comments

  3. Dan Guzman 9,241 Reputation points
    2021-01-26T12:25:56.45+00:00

    Adding to the other answers, if your intent is to return results ordered by ROWNUM, you need to add ORDER BY ROWNUM to the end of the outer query. Results might be returned in the desired order without it but the behavior is not guaranteed.

    Also be aware the ROWNUM value will be arbitrary when the CASE expression returns NULL.

    0 comments No comments

  4. EchoLiu-MSFT 14,591 Reputation points
    2021-01-27T03:30:56.307+00:00

    Hi @Murugan Andezuthu Dharmaratnam

    Please refer to:

    declare @OrderBy nvarchar(32) = 'PurchaseOrderNumber'  
    ;with cte  
    as(select ROW_NUMBER() OVER (ORDER BY POData.PurchaseOrderNumber) AS ROW_NUM,  
    Received.QuantityReceived,POData.*  
    from POData inner join Received on POData.Id = Received.PODataId  
    where @OrderBy = 'PurchaseOrderNumber'  
    union all  
    select ROW_NUMBER() OVER (ORDER BY (select distinct null from POData)) AS ROW_NUM,  
    Received.QuantityReceived,POData.*  
    from POData inner join Received on POData.Id = Received.PODataId  
    where @OrderBy<>'PurchaseOrderNumber')  
      
    select top(10)* from cte  
    order by ROW_NUM  
    option (recompile)  
    

    If you have any question, please feel free to let me know.
    If the response is helpful, please click "Accept Answer" and upvote it.

    Regards
    Echo


    If the answer is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.