sqlsql-serverstored-proceduresalias

Use aliases in CASE in stored procedure


I have SQL Server stored procedure:

Select 
    A.Id, A.Title, A.BriefText, 
    I.ArticleView, U.DisplayName AS Author, 
    U.Avatar,
    (SELECT COUNT(*) FROM Articles_Like WHERE ArticleId = A.Id) AS Likes,
    (SELECT COUNT(*) FROM Articles_Comment WHERE ArticleId = A.Id) AS Comments,
    CASE 
        WHEN EXISTS (SELECT Id FROM Articles_Like 
                     WHERE ArticleId = A.Id AND UserId = @UserId) 
            THEN 1 
            ELSE 0 
    END AS IsLiked,
    CASE 
        WHEN EXISTS (SELECT Id FROM Articles_Bookmark 
                     WHERE ArticleId = A.Id AND UserId = @UserId)
            THEN 1 
            ELSE 0 
    END AS IsBookmarked
FROM 
    Articles A
LEFT OUTER JOIN 
    Articles_Info I ON I.ArticleId = A.Id 
LEFT OUTER JOIN 
    Users_Info U ON U.UserId = A.UserId
GROUP BY 
    A.Title, A.BriefText, A.Id, I.ArticleView, 
    U.DisplayName, U.Avatar, U.UserId 
ORDER BY 
    CASE WHEN @orderby = 1 THEN A.Id END DESC,
    CASE WHEN @orderby = 2 THEN I.ArticleView END DESC,
    END DESC
    CASE WHEN @orderby = 3 THEN Likes END DESC

On the last line

 CASE WHEN @orderby = 3 THEN Likes END DESC

I get an error:

Invalid column name

when I use CASE aliases is not working anymore, how can I fix this?

I can use:

CASE WHEN @orderby = 3 THEN (SELECT COUNT(*) FROM Articles_Like WHERE ArticleId = A.Id) END DESC

Like this:

Select A.Id, A.Title,  A.BriefText, I.ArticleView, U.DisplayName AS Author, U.Avatar,
    (SELECT COUNT(*) FROM Articles_Like WHERE ArticleId=A.Id) AS Likes,
    (SELECT COUNT(*) FROM Articles_Comment WHERE ArticleId=A.Id) AS Comments,
    CASE WHEN EXISTS (SELECT Id FROM Articles_Like WHERE ArticleId=A.Id AND UserId=@UserId) 
         THEN 1 ELSE 0 END AS IsLiked,
    CASE WHEN EXISTS (SELECT Id FROM Articles_Bookmark WHERE ArticleId=A.Id AND UserId=@UserId)
         THEN 1 ELSE 0 END AS IsBookmarked
FROM Articles A
LEFT OUTER JOIN Articles_Info I ON I.ArticleId = A.Id 
LEFT OUTER JOIN Users_Info U ON U.UserId = A.UserId
GROUP BY A.Title, A.BriefText, A.Id, I.ArticleView, U.DisplayName, U.Avatar, U.UserId 
ORDER BY 
    CASE WHEN @orderby=1 THEN A.Id END DESC,
    CASE WHEN @orderby=2 THEN I.ArticleView END DESC,
    CASE WHEN @orderby=3 THEN (SELECT COUNT(*) FROM Articles_Like WHERE ArticleId=A.Id) END DESC

and it works but I used this code at the top of the stored precedure and it's not a right way to use a code twice.

I'm using SQL Server 2022


Solution

  • From the sql server docs


    If the ORDER BY clause references a column alias from the select list, the column alias must be used on its own, and not as a part of some expression in ORDER BY clause, for example:

    Copy
    SELECT SCHEMA_NAME(schema_id) AS SchemaName
    FROM sys.objects
    ORDER BY SchemaName; -- correct
    
    SELECT SCHEMA_NAME(schema_id) AS SchemaName
    FROM sys.objects
    ORDER BY SchemaName + ''; -- wrong
    

    You can see the same situation you are encountering with the following working/non-working example:

    CREATE TABLE test (id int, f1 varchar(20));
    INSERT INTO test VALUES (1, 'foo');
    INSERT INTO test VALUES (2, 'bar');
    
    /*runs fine*/
    SELECT id, (SELECT count(*) FROM test) as total_count
    FROM test
    ORDER BY total_count
    
    /*errors*/
    SELECT id, (SELECT count(*) FROM test) as total_count
    FROM test
    ORDER BY CASE WHEN 1=1 THEN total_count END
    

    dbfiddle here of this in action.


    To avoid code duplication, consider tossing the query without the ORDER BY into a subquery and then ordering in the main query:

    SELECT * 
    FROM 
      (
        Select A.Id, A.Title,  A.BriefText, I.ArticleView, U.DisplayName AS Author, U.Avatar,
            (SELECT COUNT(*) FROM Articles_Like WHERE ArticleId=A.Id) AS Likes,
            (SELECT COUNT(*) FROM Articles_Comment WHERE ArticleId=A.Id) AS Comments,
            CASE WHEN EXISTS (SELECT Id FROM Articles_Like WHERE ArticleId=A.Id AND UserId=@UserId) 
                 THEN 1 ELSE 0 END AS IsLiked,
            CASE WHEN EXISTS (SELECT Id FROM Articles_Bookmark WHERE ArticleId=A.Id AND UserId=@UserId)
                 THEN 1 ELSE 0 END AS IsBookmarked
        FROM Articles A
        LEFT OUTER JOIN Articles_Info I ON I.ArticleId = A.Id 
        LEFT OUTER JOIN Users_Info U ON U.UserId = A.UserId
        GROUP BY A.Title, A.BriefText, A.Id, I.ArticleView, U.DisplayName, U.Avatar, U.UserId 
      ) dt
    ORDER BY 
            CASE WHEN @orderby=1 THEN Id END DESC,
            CASE WHEN @orderby=2 THEN ArticleView END DESC,
            CASE WHEN @orderby=3 THEN Likes END DESC