使用case表达式的order by子句

时间:2012-07-03 17:57:02

标签: sql-server-2008

我传递了两个参数:

@Column和@Direction,它们都是nvarchar(50)

我想根据参数中的值更改select查询的顺序。

例如,

if @Column = Name and @Direction = 'Desc' then order by Name desc
if @Column = Name and @Direction = 'Asc'  then order by Name asc
if @Column = Type and @Direction = 'Desc' then order by Type desc
if @Column = Type and @Direction = 'Asc'  then order by Type asc

我的桌子叫做汽车。

它有三列:CarId,Name,Type

我试图这样做,但它没有返回正确的结果(就像忽略了order by顺序)

select * from Cars
order by        
case 
when @SortColumn = 'Type' and @SortDir = 'Desc' then 1
when @SortColumn = 'Type' and @SortDir = 'Desc' then 1
when @SortColumn = 'Name' and @SortDir = 'Asc'  then 3
when @SortColumn = 'Name' and @SortDir = 'Asc'  then 3, end asc

2 个答案:

答案 0 :(得分:1)

CASE是一个产生单个值的表达式。它不能像您在其他语言中那样用于控制流,或者您可以尝试使用IF。您也无法在查询中使用IF

以下分别处理每个条件。如果条件不匹配,则该表达式的结果为NULL,在任何排序中都会忽略该结果。因此,这些条款的顺序是无关紧要的,因为只会观察到所有条件都为真的条款:

ORDER BY 
  CASE WHEN @SortColumn = 'Type' AND @SortDir = 'DESC' THEN Type END DESC,
  CASE WHEN @SortColumn = 'Name' AND @SortDir = 'DESC' THEN Name END DESC,
  CASE WHEN @SortColumn = 'Type' AND @SortDir = 'ASC'  THEN Type END,
  CASE WHEN @SortColumn = 'Name' AND @SortDir = 'ASC'  THEN Name END;

这些可能会稍微合并,但我不知道数据类型,所以这可能是最安全的。如果数据类型相同,则:

ORDER BY 
  CASE WHEN @SortDir = 'DESC' THEN 
    CASE WHEN @SortColumn = 'Type' THEN Type ELSE Name END 
  END DESC,
  CASE WHEN @SortDir = 'ASC' THEN
    CASE WHEN @SortColumn = 'Type' THEN Type ELSE Name END
  END;

由于您无法在一个表达式中执行条件ASCDESC,因此无法进一步折叠。但您可以通过其他方式简化,例如以下查询将产生与上面相同的结果,即使在某些情况下,order by会尝试(例如)ORDER BY Type DESC, Type DESC - 我不是100%确定优化器是否将其简化为冗余但是它应该崩溃单个排序运算符(需要时 - 有时此查询可以根据用于满足查询的索引产生固有排序)。

ORDER BY 
  CASE WHEN @SortColumn = 'Type' AND @SortDir = 'DESC' THEN Type END DESC,
  CASE WHEN @SortColumn = 'Name' AND @SortDir = 'DESC' THEN Name END DESC,
  CASE WHEN @SortColumn = 'Type' THEN Type END,
  CASE WHEN @SortColumn = 'Name' THEN Name END;

...或...

ORDER BY 
  CASE WHEN @SortDir = 'DESC' THEN 
    CASE WHEN @SortColumn = 'Type' THEN Type ELSE Name END 
  END DESC,
  CASE WHEN @SortColumn = 'Type' THEN Type ELSE Name END;

你也可以考虑动态SQL,因为如果这变得复杂得多,那么这里有很多维护。

DECLARE @sql NVARCHAR(MAX);
SET @sql = N'SELECT ... ORDER BY ' + @SortColumn + ' ' + @SortDir;
PRINT @sql;
-- EXEC sp_executesql @sql;

当然这可能会引入其他问题(查询其余部分的可维护性,计划缓存膨胀......)

答案 1 :(得分:1)

您可以尝试这种方法:

WITH ranked AS (
  SELECT
    ...
    rnk = ROW_NUMBER() OVER (
      ORDER BY
        CASE @SortColumn
          WHEN 'Type' THEN Type
          WHEN 'Name' THEN Name
        END
    ) * CASE @Direction WHEN 'DESC' THEN -1 ELSE 1 END
  FROM ...
)
SELECT
  ...
FROM ranked
ORDER BY rnk

ROW_NUMBER()函数在@SortColumn参数指定的列上生成排名,使用CASE表达式解析名称,如@Aaron Bertrand's answer中所示,另一个CASE表达式用于还原如果-1@Direction,则将排名乘以'DESC'。生成的排名列可以在主查询的ORDER BY子句中使用,无需任何调整。