Order By 与 Case 语句一起使用

Order By Used with Case Statement

我使用 Order By Used with Case Statement,这可能是我收到错误的原因 Windowed functions can only appear in the SELECT or ORDER BY clauses.

这是我的完整 sql,其中我使用 Row_Number 函数进行分页和排序。请指导我哪里出错了。

DECLARE @StartIndex INT
DECLARE @EndIndex   INT

DECLARE @SortColumn VARCHAR(MAX)
DECLARE @SortDirection VARCHAR(MAX)

SET @StartIndex = 1
SET @EndIndex = 20
SET @SortColumn = 'OrderID'
SET @SortColumn = 'A'

SELECT * FROM Orders
WHERE
  ROW_NUMBER() OVER 
  (
    ORDER BY
        CASE (@SortColumn + ':' + @SortDirection)
            WHEN 'OrderID:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID ASC)
            WHEN 'OrderID:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID DESC)
            WHEN 'CustomerID:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.CustomerID ASC)
            WHEN 'CustomerID:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.CustomerID DESC)
            WHEN 'EmployeeID:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.EmployeeID ASC)
            WHEN 'EmployeeID:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.EmployeeID DESC)
            WHEN 'OrderDate:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderDate ASC)
            WHEN 'OrderDate:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderDate DESC)
            WHEN 'RequiredDate:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.RequiredDate ASC)
            WHEN 'RequiredDate:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.RequiredDate DESC)
            WHEN 'ShippedDate:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID ASC)
            WHEN 'ShippedDate:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID DESC)
            WHEN 'ShipVia:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipVia ASC)
            WHEN 'ShipVia:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipVia DESC)
            WHEN 'Freight:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.Freight ASC)
            WHEN 'Freight:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.Freight DESC)
            WHEN 'ShipName:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipName ASC)
            WHEN 'ShipName:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipName DESC)
            WHEN 'ShipAddress:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipAddress ASC)
            WHEN 'ShipAddress:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipAddress DESC)
            WHEN 'ShipCity:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipCity ASC)
            WHEN 'ShipCity:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipCity DESC)
            WHEN 'ShipRegion:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipRegion ASC)
            WHEN 'ShipRegion:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipRegion DESC)
            WHEN 'ShipPostalCode:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipPostalCode ASC)
            WHEN 'ShipPostalCode:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipPostalCode DESC)
            WHEN 'ShipCountry:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipCountry ASC)
            WHEN 'ShipCountry:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipCountry DESC)

        END 
  ) BETWEEN (@StartIndex - 1) * @EndIndex + 1 AND @StartIndex * @EndIndex 
  /* AND more conditions ... */
ORDER BY
  CASE WHEN @SortDirection = 'A' THEN
    CASE @SortColumn 
        WHEN 'OrderID'    THEN OrderID
        WHEN 'CustomerID' THEN CustomerID
        WHEN 'EmployeeID' THEN CustomerID      
        WHEN 'OrderDate' THEN CustomerID
        WHEN 'RequiredDate' THEN CustomerID
        WHEN 'ShippedDate' THEN CustomerID
        WHEN 'ShipVia' THEN CustomerID
        WHEN 'Freight' THEN CustomerID
        WHEN 'ShipName' THEN CustomerID
        WHEN 'ShipAddress' THEN CustomerID
        WHEN 'ShipCity' THEN CustomerID
        WHEN 'ShipRegion' THEN CustomerID
        WHEN 'ShipPostalCode' THEN CustomerID
        WHEN 'ShipCountry' THEN CustomerID
    END
  END,
  CASE WHEN @SortDirection = 'D' THEN
    CASE @SortColumn 
        WHEN 'OrderID'    THEN OrderID
        WHEN 'CustomerID' THEN CustomerID
        WHEN 'EmployeeID' THEN CustomerID      
        WHEN 'OrderDate' THEN CustomerID
        WHEN 'RequiredDate' THEN CustomerID
        WHEN 'ShippedDate' THEN CustomerID
        WHEN 'ShipVia' THEN CustomerID
        WHEN 'Freight' THEN CustomerID
        WHEN 'ShipName' THEN CustomerID
        WHEN 'ShipAddress' THEN CustomerID
        WHEN 'ShipCity' THEN CustomerID
        WHEN 'ShipRegion' THEN CustomerID
        WHEN 'ShipPostalCode' THEN CustomerID
        WHEN 'ShipCountry' THEN CustomerID
    END 
  END DESC

编辑 1

我按照您的代码更改了我的代码,但仍然出现相同的错误。这是新代码

DECLARE @StartIndex INT
DECLARE @EndIndex   INT

DECLARE @SortColumn VARCHAR(MAX)
DECLARE @SortDirection VARCHAR(MAX)

SET @StartIndex = 1
SET @EndIndex = 20
SET @SortColumn = 'CustomerID'
SET @SortDirection = 'D'

;WITH cte as(SELECT *, 
                    ROW_NUMBER() OVER 
                    (ORDER BY 
                        CASE (@SortColumn + ':' + @SortDirection)
                            WHEN 'OrderID:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID ASC)
                            WHEN 'OrderID:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID DESC)
                            WHEN 'CustomerID:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.CustomerID ASC)
                            WHEN 'CustomerID:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.CustomerID DESC)
                            WHEN 'EmployeeID:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.EmployeeID ASC)
                            WHEN 'EmployeeID:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.EmployeeID DESC)
                            WHEN 'OrderDate:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderDate ASC)
                            WHEN 'OrderDate:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderDate DESC)
                            WHEN 'RequiredDate:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.RequiredDate ASC)
                            WHEN 'RequiredDate:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.RequiredDate DESC)
                            WHEN 'ShippedDate:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID ASC)
                            WHEN 'ShippedDate:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID DESC)
                            WHEN 'ShipVia:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipVia ASC)
                            WHEN 'ShipVia:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipVia DESC)
                            WHEN 'Freight:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.Freight ASC)
                            WHEN 'Freight:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.Freight DESC)
                            WHEN 'ShipName:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipName ASC)
                            WHEN 'ShipName:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipName DESC)
                            WHEN 'ShipAddress:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipAddress ASC)
                            WHEN 'ShipAddress:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipAddress DESC)
                            WHEN 'ShipCity:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipCity ASC)
                            WHEN 'ShipCity:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipCity DESC)
                            WHEN 'ShipRegion:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipRegion ASC)
                            WHEN 'ShipRegion:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipRegion DESC)
                            WHEN 'ShipPostalCode:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipPostalCode ASC)
                            WHEN 'ShipPostalCode:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipPostalCode DESC)
                            WHEN 'ShipCountry:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipCountry ASC)
                            WHEN 'ShipCountry:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.ShipCountry DESC)
                        END 
                     ) rn
             FROM Orders)
SELECT * FROM cte
WHERE rn BETWEEN (@StartIndex - 1) * @EndIndex + 1 AND @StartIndex * @EndIndex 

如您所知,您不能在 where 子句中使用 window 函数。只需在cte或子查询中进行,然后在外部进行过滤即可:

;WITH cte as(SELECT *, 
                    ROW_NUMBER() OVER 
                    (ORDER BY CASE WHEN (@SortColumn + ':' + @SortDirection) = 'OrderID:A' 
                                   THEN Orders.OrderID ASC END,
                              CASE WHEN (@SortColumn + ':' + @SortDirection) = 'OrderID:D' 
                                   THEN Orders.OrderID DESC END,
                              ...) rn
             FROM Orders)
SELECT * FROM cte
WHERE rn BETWEEN (@StartIndex - 1) * @EndIndex + 1 AND @StartIndex * @EndIndex 
  /* AND more conditions ... */
ORDER BY
...
--same case expressions here

正如您所注意到的,您将不得不拥有多个而不是一个大的 case 表达式,因为您不能在一个 case 表达式中使用不同的类型,否则会出现转换错误。

但根据我的经验,最好使用带有参数化参数的动态 sql,因为 sql 引擎会为每个案例保存执行计划,并且比拥有如此大的复杂程序要快得多查询。

动态版本:

DECLARE @sql NVARCHAR(MAX)

SET @sql = ';WITH cte as(SELECT *, 
                    ROW_NUMBER() OVER 
                    (ORDER BY ' + CASE (@SortColumn + ':' + @SortDirection) 
                                    WHEN 'OrderID:A' THEN 'Orders.OrderID ASC'
                                    WHEN 'OrderID:D' THEN 'Orders.OrderID DESC'
                                    ...
                                    WHEN 'ShipCountry:D' THEN 'Orders.ShipCountry DESC' END + 
                              ') rn
             FROM Orders)
SELECT * FROM cte
WHERE rn BETWEEN ' + CAST((@StartIndex - 1) * @EndIndex + 1 AS NVARCHAR(MAX)) + ' AND ' + 
                     CAST(@StartIndex * @EndIndex AS NVARCHAR(MAX))

EXEC(@sql)