Dynamic sorting with T-SQL

This is a migrated post from my former blog at bloodsweatand.net. It was originally posted 2008-10-27.

Almost all stored procedures that I write whose job it is to fetch data has to sort the data according to one or several columns. More often than not the selection of those columns, and the sort direction, is determined by the web application and therefore has to be based on parameters.

One simple and quite common solution to this is to build the whole query as a string and execute it with EXEC or sp_executesql. This way the stored procedure doesn't have to know anything about the sort order and the web application is free to specify exactly how it wants the data sorted. This comes at a price however as the database server won’t be able to cache the query plan for the query and the web application has to deal with building SQL,which goes against the separation of concerns between the application layer and the storage layer.

I've spent quite some time researching other ways to do dynamic sorting with T-SQL without using dynamic SQL. Sadly I've been unable to find a perfect solution that can do everything we can when using dynamic SQL. However, if we are willing to limit ourselves to a fixed number of sortable columns it's possible to achieve otherwise completely dynamic sorting by using several CASE statements.

Let's say we have a table with images in it. We will want to sort either by file name or by upload date. Our stored procedure may then look something like this:

CREATE PROCEDURE [GetImages]    
     @intSortField int = 0 --0 = Name, 1 = UploadDate   
    ,@intSortDirection int = 0 --0 = Ascending, 1 = Descending  
AS  
BEGIN   
    SET NOCOUNT ON;   
    SELECT ID, FileName, UploadDate   FROM Images   
    ORDER BY     
    CASE WHEN @SortField = 0 AND @SortDirection = 1 THEN FileName END DESC,    
    CASE WHEN @SortField = 0 THEN FileName END,   
    CASE WHEN @SortField = 1 AND @SortDirection = 1 THEN UploadDate END DESC,    
    CASE WHEN @SortField = 1 THEN UploadDate END,    
    ID  -- Use ID as a secondary sort field
END

The parameters @SortField and @sortDirection may map against two enums in the web application such as these:

public enum ImageSortField
{
     FileName = 0
    ,UploadDate = 1
}
public enum ImageSortDirection
{
     Ascending = 0
    ,Descending = 1
}

If we want to be able to specify two columns to sort by we can simply add two extra parameters to our stored procedure:

CREATE PROCEDURE [GetImages]    
     @intPrimarySortField int = 0 --0 = Name, 1 = UploadDate   
    ,@intPrimarySortDirection int = 0 --0 = Ascending, 1 = Descending  
    ,@intSecondarySortField int = 0
    ,@intSecondarySortDirection int = 0
AS  
BEGIN   
    SET NOCOUNT ON;   
    SELECT ID, FileName, UploadDate   FROM Images   
    ORDER BY     
    CASE WHEN @intPrimarySortField = 0 AND @intPrimarySortDirection = 1 THEN FileName END DESC,    
    CASE WHEN @intPrimarySortField = 0 THEN FileName END,   
    CASE WHEN @intPrimarySortField = 1 AND @intPrimarySortDirection = 1 THEN UploadDate END DESC,    
    CASE WHEN @intPrimarySortField = 1 THEN UploadDate END,   
    CASE WHEN @intSecondarySortField = 0 AND @intSecondarySortDirection = 1 THEN FileName END DESC,    
    CASE WHEN @intSecondarySortField = 0 THEN FileName END,   
    CASE WHEN @intSecondarySortField = 1 AND @intSecondarySortDirection = 1 THEN UploadDate END DESC,    
    CASE WHEN @intSecondarySortField = 1 THEN UploadDate END,    
    ID  -- Use ID as a secondary sort field
END

When you have more columns you wish to be able to sort by and/or want a tertiary sort column this method will result in a pretty long stored procedure. In my opinion it is however worth it to achieve separation of concerns between the application and the database. I’ll leave the performance comparison between the two methods discussed here for another article.

PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.

Comments

  1. xr280xr's avatar

    xr280xr 12 months ago

    Thanks for the example. I have found several similar examples on the internet, but what if you want to order by more than one column. I want to order by 6 in my procedure.

    E.G.

    Select * from x
    order by column 1, column 2, column 3

  2. Joel Abrahamsson's avatar

    Joel Abrahamsson 12 months ago

    Well in the example above I order by two columns (@intPrimarySortField and @intSecondarySortField). It wont look pretty but you could easily add a third, a fourth and so on.

Add a comment

Allowed tags: <b>, <em>, <quote cite="">, <code>, <c-sharp-code>, <css-code>, <sql-code>, <xml-code>, <javascript-code>. If you want to display code examples, please remember to write &lt; for < and &gt; for >.

Follow me on Twitter

  1. Bookmarked: Google Scribe http://bit.ly/9iDp8s 2 days ago
  2. Blogged: The future of EPiMVC http://bit.ly/ck6EPg #episerver 2 days ago
  3. At Skånegläntan, as always #parentalleave 2 days ago
follow me

Latest comments

  1. Svante wrote "Yes, I noticed that it was a singleton, and I guess the real..." on Something to beware of when using EPiAbstractions and an IoC container
  2. Joel Abrahamsson wrote "Well, first of all you wont get any arguments from me regard..." on Something to beware of when using EPiAbstractions and an IoC container
  3. Svante wrote "Hmm... Since the issue really is with the public instance co..." on Something to beware of when using EPiAbstractions and an IoC container

About this site

This blog is built with EPiServer Community, EPiServer CMS, ASP.NET MVC and a bunch of other great products. The source code is available for download at the projects page, where you also can read more about this site and my other projects.

read more