1

This SQL statement is being flagged in SQL Server in the ORDER BY ' + @SortField + ' ' + @SortOrder + ' part.. Any ideas how can I fix it?

ALTER PROC [sbuser].[sp_MemberMailList ]
@MemberMailID bigint = null,
@FromMemberID bigint = null,
@ToMemberID bigint = null,
@Subject varchar(150) = null,
@Message varchar(8000) = null,
@FromDeletedFlag bit = null,
@ToDeletedFlag bit = null,
@FromArchivedFlag bit = null,
@ToArchivedFlag bit = null,
@ReadFlag bit = null,
@SQL nvarchar(4000) = null,
@SortField varchar(100) = null,
@SortOrder varchar(25) = null,
@NotificationSent bit = null,
@MemberID bigint = null,
@OnHold bit = 0,
@SpecialMail varchar(1) = 'N',
@PageSize float = null,
@PageNum int = 1,
@TotalPages float = null,
@StartDate datetime = null,
@EndDate datetime = null,
@MODE varchar(50)

AS

IF @MODE = 'INBOX-MAIL'
BEGIN
    SELECT @TotalPages = COUNT(*)/@PageSize
    FROM MemberMail a
    INNER JOIN Member b ON b.MemberID = a.FromMemberID
    WHERE a.ToMemberID = @ToMemberID
    AND a.ToDeletedFlag = 0
    AND a.OnHold = 0
    AND a.ToArchivedFlag = 0;

    WITH InMails AS
    (
        SELECT ROW_NUMBER() OVER(ORDER BY ' + @SortField + ' ' + @SortOrder + ') AS RowNum,
        a.MemberMailID,
        a.FromMemberID,
        a.Subject,
        a.CreateDate,
        b.UserName,
        a.ToReadFlag,
        b.Firstname,
        b.Lastname,
        b.MemberDisplayName AS DisplayName
        FROM MemberMail a
        INNER JOIN Member b ON b.MemberID = a.FromMemberID
        WHERE a.ToMemberID = @ToMemberID
        AND a.ToDeletedFlag = 0
        AND a.OnHold = 0
        AND a.ToArchivedFlag = 0
    )
    SELECT * 
    FROM InMails
    WHERE RowNum BETWEEN (@PageNum - 1) * @PageSize + 1 AND @PageNum * @PageSize
    ORDER BY CreateDate DESC
END

Any help would be appreciated..

Many thanks

3 Answers 3

1

You can't specify a field or sort direction with a variable. The entire SQL statement needs to be a dynamic string to do this.

So

SELECT ROW_NUMBER() OVER(
    ORDER BY [field expression] asc|desc) AS RowNum

Is fine, as is

exec('SELECT ROW_NUMBER() OVER(' + 
    'ORDER BY ' + @SortField + ' ' + @SortOrder + ') AS RowNum ...')

But

SELECT ROW_NUMBER() OVER(
    ORDER BY ' + @SortField + ' ' + @SortOrder + ') AS RowNum

Can't be handled by the query compiler in SQL.

Sign up to request clarification or add additional context in comments.

4 Comments

Any ideas how I can resolve this? Could really use the help here please.
@neojakey - either of the first two options will work. Basically you have either static fields in the order by, or the entire statement has to be a dynamic string. This is because what you choose to order by can completely change the query plan and how it's optimised, so there's no value to SQL in caching it.
@neojakey - there is one way to get round that, but it's nasty. Your sproc can only have sort fields that are in your MemberMail or Member tables, so you could repeat the entire statement for each sortable field. That'll be very ugly code, but will give SQL query plans that it can cache.
I repeated the entire statement 2 times for ASC and DESC. Many thanks for your assistance..!
1
SELECT ROW_NUMBER() OVER(ORDER BY ' + @SortField + '   ' + @SortOrder + ') AS RowNum,
                                  ^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^
                                    string #1              string #2

                                                      ^---no operator

Unless you've got an outer string not shown in your code sample, you cannot build up an order by clause like this. You've got two different quoted strings with a space between then.

Comments

1

It looks like you're trying to dynamically changing the fields ORDER BY the ROW_NUMBER() ranking function. You can't do it the way you're trying.

You need to use dynamic SQL to accomplish what you're trying. You should read The Curse and Blessings of Dynamic SQL to get a good understanding of the mechanics as well as how to protect yourself from SQL Injection.

If for some reason you don't want to use dynamic SQL you can generate a giant case statement. For example this sample below taken from gbn's answer to Dynamic order direction.

SELECT
     Columns you actually want
FROM
    (
    SELECT
         Columns you actually want,
         ROW_NUMBER() OVER (ORDER BY AddedDate) AS AddedDateSort,
         ROW_NUMBER() OVER (ORDER BY Visible) AS VisibleSort,
         ROW_NUMBER() OVER (ORDER BY AddedBy) AS AddedBySort,
         ROW_NUMBER() OVER (ORDER BY Title) AS TitleSort
    FROM
         myTable
    WHERE
         MyFilters...
    ) foo
ORDER BY
     CASE @OrderByColumn
        WHEN 'AddedDate' THEN AddedDateSort
        WHEN 'Visible' THEN VisibleSort    
        WHEN 'AddedBy' THEN AddedBySort
        WHEN 'Title' THEN TitleSort
     END * @multiplier;

If you do that you should be on the lookout for performance issues since indexes may not be used for the sorting.

1 Comment

Nice solution - you could simplify it further with a sub query where the case switch is a selection. Then you have the row_number() function across that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.