8

Northwind データベースを使用していて、ストアド プロシージャを介してクエリを実行したいとします。このストアド プロシージャには、次のパラメータが含まれています。

  • @Offsetページネーションの開始位置を示すために、
  • @Limitページサイズを示すために、
  • @SortColumnソート目的で使用される列を示すために、
  • @SortDirection、アセンダントまたはディセンダントの並べ替えを示します。

結果セットには数千行が含まれているため、キャッシングはオプションではないため、データベースでページネーションを行うという考え方です(そして、VIEWSTATEの使用は、IMOが悪いとは見なされません)。

ご存知かもしれませんが、SQL Server 2005 には、結果セットのパーティション内の行の連続番号を返す関数ROW_NUMBERが用意されており、各パーティションの最初の行を 1 として開始します。

返されたすべての列 (この例では 5 つ) で並べ替えを行う必要があり、動的 SQL はオプションではないため、2 つの可能性があります。大量のクエリを使用し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

さまざまな引数を使用してクエリを数回試しましたが、そのパフォーマンスは実際には非常に優れていますが、他の方法で最適化されているように見えます。

このクエリに何か問題がありますか、それともこのようにしますか? 別のアプローチを提案しますか?

4

1 に答える 1

5

単純:

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) で並べ替えられ、その逆も同様です。

同じ ORDER BY 式に対して ROW_NUMBER() 関数を機能させます。

于 2008-10-23T15:21:10.707 に答える