SQL Server窗口函数子句中的条件排序顺序

时间:2011-08-26 10:03:56

标签: sql sql-server tsql

所以,这不是你的平均'有条件排序'问题......我这里有一个相当棘手的问题。 :-)我想允许我的存储过程为结果提供条件排序顺序。通常,这可以通过以下方式完成:

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

我想围绕实际CASE / ASC做一个DESC声明,但这不起作用。上述方法有效的原因是,当@SortAscending不等于给定值时,SQL Server会将CASE语句转换为常量NULL。因此,如果@SortAscending为0,则实际上有:

ORDER BY
    NULL ASC,
    SortColumn DESC

然后,第一个排序表达式什么都不做。这是有效的,因为在常规SELECT语句中,您可以在ORDER BY子句中使用常量。

麻烦的是,我在存储过程中排序的时间是在包含窗口函数SELECT的{​​{1}}语句中。因此,我想将ROW_NUMBER()语句放在其CASE子句中,如下所示:

OVER

不幸的是,这会在运行存储过程时导致以下错误:

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

因为这是窗口函数的子句,所以Windowed functions do not support constants as ORDER BY clause expressions. 语句到常量CASE的转换无效。

有人可以想到一种方法,我可以有条件地改变NULL ed UNION的排序顺序,并为这些排序结果的每一行分配行号吗?我知道我可以将整个查询构造为字符串并将其作为完全动态的SQL执行,但如果可能的话,我宁愿避免使用它。


更新:看起来这个问题不是由SELECT语句本身造成的,而是因为我只使用了常量值CASE语句的条件子句。我已经开始提出一个关于这种奇怪行为的新问题here

6 个答案:

答案 0 :(得分:3)

如果将它们包装在SELECT中,则可以使用常量,例如:

OVER( ORDER BY (SELECT NULL) )

所以在你的情况下你应该能够做到:

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, 

答案 1 :(得分:1)

您可以在两个方向指定行号,并在外部order by中选择一个:

select  *
from    (
        select  row_number() over (order by SortColumn) rn1
        ,       row_number() over (order by SortColumn) rn2
        ,       *
        from    @t
        ) as SubQueryAlias
order by
        case when @asc=1 then rn1 end
,       case when @asc=0 then rn2 end desc

SE Data的工作示例。

答案 2 :(得分:1)

你可以

  • 将升序和降序列添加到中间结果
  • 最后选择其中一个。

SQL语句

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

答案 3 :(得分:1)

如果您打算将这些行号用作其他条件逻辑的一部分,那么这样的事情可能会起作用:

CASE WHEN @SortAscending=1 THEN COUNT(*) OVER() + 1 ELSE 0 END +
(CASE WHEN @SortAscending=1 THEN -1 ELSE 1 END *
    ROW_NUMBER() OVER (ORDER BY SortColumn DESC)) as RowNumber

这甚至可以进行扩展,这样如果您使用PARTITION子句,只要两个OVER()表达式都使用相同的PARTITION子句,它就会继续有效。

答案 4 :(得分:0)

DECLARE @sign int = -1;
IF @SortAscending = 0 SET @sign = -1;

SELECT ROW_NUMBER() OVER (ORDER BY RowNumber) AS RN,
    *
INTO #ResultsBeforeSubset
FROM (
    SELECT
        @sign * ROW_NUMBER() OVER (ORDER BY rowValues.[SortColumn]) AS RowNumber,
        *
    FROM MyTable
) rowValuesWithRowNum
ORDER BY RN

--DECLARE @sign int = 1;
--IF @SortAscending = 0 SET @sign = -1;
--
--SELECT *
--INTO #ResultsBeforeSubset
--FROM (
--    SELECT
--        @sign * ROW_NUMBER() OVER (ORDER BY rowValues.[SortColumn] AS RowNumber,
--        *
--    FROM MyTable
--) rowValuesWithRowNum
--ORDER BY RowNumber;

答案 5 :(得分:0)

您可以在order by中使用窗口功能,而不仅仅是在选择中。窗口函数row_number,rank,dense_rank,都返回一个数字(bigint),您可以将其乘以负值以获得反向...

SELECT *
INTO #ResultsBeforeSubset
FROM MyTable
ORDER BY
  (rank() over (order by SortColumn)) * case when @asc=1 then 1 else -1 end