Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional sort order in SQL Server windowed function clauses

So, this is not your average 'conditional sort by' question... I have a rather tricky problem here. :-) I want to allow my stored procedure to offer a conditional sort order for the results. Normally this can be done in the following manner:

SELECT *
INTO #ResultsBeforeSubset
FROM
    MyTable
ORDER BY
    CASE WHEN @SortAscending=1 THEN 'SortColumn' END ASC,
    CASE WHEN @SortAscending=0 THEN 'SortColumn' END DESC

I'd like to do a CASE statement around the actual ASC/DESC, but that doesn't work. The reason the above method works is because, when @SortAscending isn't equal to the given value, SQL server translates the CASE statement into the constant NULL. So, if @SortAscending is 0, you effectively have:

ORDER BY
    NULL ASC,
    SortColumn DESC

The first sort expression, then, just does nothing. This works because in a regular SELECT statement you can use constant in an ORDER BY clause.

Trouble is, the time that I'm sorting in my stored proc is during a SELECT statement which contains a windowed function ROW_NUMBER(). I therefore want to put the CASE statement inside its OVER clause, like so:

SELECT *
INTO #ResultsBeforeSubset
FROM (
    SELECT
        ROW_NUMBER() OVER (
            ORDER BY
                CASE WHEN @SortAscending=1 THEN rowValues.[SortColumn] END ASC,
                CASE WHEN @SortAscending=0 THEN rowValues.[SortColumn] END DESC
        ) AS RowNumber,
        *
    FROM (
        -- UNIONed SELECTs returning rows go here...
    ) rowValues
) rowValuesWithRowNum

Unfortunately, this causes the following error when you run the stored procedure:

Windowed functions do not support constants as ORDER BY clause expressions.

Because this is the clause of a windowed function, the conversion of the CASE statement to the constant NULL is invalid.

Can anyone think of a way that I can conditionally vary the sort order of UNIONed SELECTs, and assign row numbers to each row resulting from these sorted results? I know I could resort to constructing the entire query as a string and execute that as fully dynamic SQL, but I'd rather avoid that if possible.


UPDATE: Looks like the problem wasn't caused by the CASE statement per se, but by the fact that I was using only constant values in the CASE statement's conditional clause. I've started up a new question on this curious behaviour here.

like image 295
Jez Avatar asked Aug 26 '11 10:08

Jez


People also ask

Can we use window function in where clause?

Window functions are permitted only in the select list and ORDER BY clause. Query result rows are determined from the FROM clause, after WHERE , GROUP BY , and HAVING processing, and windowing execution occurs before ORDER BY , LIMIT , and SELECT DISTINCT .

What is happening when you omit the ORDER BY clause when doing aggregates with window functions?

If the window ORDER BY clause is omitted, then rows are processed in an unspecified order so that the results of any window function invoked in this way would be unpredictable and therefore meaningless. Aggregation functions invoked in this way might be sensitive to what the window ORDER BY clause says.

What is the correct syntax for a windowing function?

Basic windowing syntax. The usual suspects: SUM, COUNT, and AVG. ROW_NUMBER() RANK() and DENSE_RANK()

How Ntile function works in SQL?

The NTILE window function divides ordered rows in the partition into the specified number of ranked groups of as equal size as possible and returns the group that a given row falls into.


1 Answers

You could use constants if you wrap them in a SELECT, such as:

OVER( ORDER BY (SELECT NULL) )

So in your case you should be able to do:

SELECT
    ROW_NUMBER() OVER (
        ORDER BY
            (SELECT CASE WHEN @SortAscending=1 THEN rowValues.[SortColumn] END) ASC,
            (SELECT CASE WHEN @SortAscending=0 THEN rowValues.[SortColumn] END) DESC
    ) AS RowNumber, 
like image 134
yoel halb Avatar answered Oct 10 '22 00:10

yoel halb