在 SQL Server 2005 中使用 ROW_NUMBER() OVER () 对不同列进行排序的分页查询

Posted

技术标签:

【中文标题】在 SQL Server 2005 中使用 ROW_NUMBER() OVER () 对不同列进行排序的分页查询【英文标题】:Paginated query using sorting on different columns using ROW_NUMBER() OVER () in SQL Server 2005 【发布时间】:2010-09-18 19:21:44 【问题描述】:

假设我正在使用 Northwind 数据库,并且我想通过包含以下参数的存储过程运行查询:

@Offset 表示分页从哪里开始, @Limit 表示页面大小, @SortColumn 表示用于排序的列, @SortDirection,表示升序或降序。

这个想法是在数据库上进行分页,因为结果集包含数千行,所以缓存不是一种选择(而且使用 VIEWSTATE 甚至不被认为是,IMO,很烂)。

您可能知道,SQL Server 2005 提供了函数ROW_NUMBER,它返回结果集分区内行的序号,每个分区的第一行从 1 开始

我们需要对每个返回的列(本例中为 5 个)进行排序,动态 SQL 不是一个选项,因此我们有两种可能性:使用大量 IF ... ELSE ... 并有 10 个查询,这很难维护,或者有如下查询:

WITH PaginatedOrders AS (
    SELECT
        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 'ShippedDate:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID ASC)
            WHEN 'ShippedDate:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID DESC)
        END AS RowNumber,
        OrderID, CustomerID, EmployeeID, OrderDate, ShippedDate
    FROM Orders
    -- WHERE clause goes here
)
SELECT
    RowNumber, OrderID, CustomerID, EmployeeID, OrderDate, ShippedDate,
    @Offset, @Limit, @SortColumn, @SortDirection
FROM PaginatedOrders
WHERE RowNumber BETWEEN @Offset AND (@Offset + @Limit - 1)
ORDER BY RowNumber

我已经多次尝试查询,使用不同的参数,它的性能实际上是相当不错的,但它仍然看起来可能会以其他方式进行优化。

这个查询有什么问题,或者你会这样做吗?您是否提出了不同的方法?

【问题讨论】:

我的愚蠢错误,已修复。请重试。 【参考方案1】:

简单:

SELECT
  OrderID, CustomerID, EmployeeID, OrderDate, ShippedDate,
  @Offset, @Limit, @SortColumn, @SortDirection
FROM
  Orders
WHERE
  ROW_NUMBER() OVER 
  (
    ORDER BY
      /* same expression as in the ORDER BY of the whole query */
  ) BETWEEN (@PageNum - 1) * @PageSize + 1 AND @PageNum * @PageSize 
  /* AND more conditions ... */
ORDER BY
  CASE WHEN @SortDirection = 'A' THEN
    CASE @SortColumn 
      WHEN 'OrderID'    THEN OrderID
      WHEN 'CustomerID' THEN CustomerID
      /* more... */
    END
  END,
  CASE WHEN @SortDirection = 'D' THEN
    CASE @SortColumn 
      WHEN 'OrderID'    THEN OrderID
      WHEN 'CustomerID' THEN CustomerID
      /* more... */
    END 
  END DESC

如果选择了 ASC 顺序,这将按 NULL (DESC) 排序,反之亦然。

让 ROW_NUMBER() 函数处理同一个 ORDER BY 表达式。

【讨论】:

今天下午我将尝试使用查询计划器和分析器的这个功能,只是为了比较结果:) 我渴望看到结果。 :-) 正是这个给出了以下错误:Msg 207, Level 16, State 1, Line 41 - Invalid column name 'RowNumber'.。然后我将使用 WITH 子句。

以上是关于在 SQL Server 2005 中使用 ROW_NUMBER() OVER () 对不同列进行排序的分页查询的主要内容,如果未能解决你的问题,请参考以下文章

ROW_NUMBER SQL Server 2005的LIMIT功能实现(ROW_NUMBER()排序函数)

SQL Server 2005 ROW_NUMBER() 没有 ORDER BY

MSSQL sql server 2005/2008 row_number()函数应用之–删除表中重

SQL Server之排序函数

SQL Server之排序函数

怎样将sqlserver2005数据库中一张表的一半数据取出来