Requête paginée utilisant le tri sur différentes colonnes à l'aide de ROW_NUMBER () OVER () dans SQL Server 2005

StackOverflow https://stackoverflow.com/questions/230058

  •  04-07-2019
  •  | 
  •  

Question

Supposons que j'utilise la base de données Northwind et que je souhaite exécuter une requête via une procédure stockée contenant, entre autres paramètres, les éléments suivants:

  • @Offset pour indiquer le début de la pagination,
  • @Limit pour indiquer la taille de la page,
  • @SortColumn pour indiquer la colonne utilisée pour le tri,
  • @SortDirection , pour indiquer le tri ascendant ou descendant.

L’idée est de faire la pagination sur la base de données, car le jeu de résultats contient des milliers de lignes. La mise en cache n’est donc pas une option (et utiliser VIEWSTATE n’est même pas considéré comme, IMO, c'est nul).

Comme vous le savez peut-être, SQL Server 2005 fournit la fonction ROW_NUMBER . renvoie le numéro séquentiel d'une ligne dans une partition d'un jeu de résultats, en commençant à 1 pour la première ligne de chaque partition .

Nous avons besoin de trier chaque colonne renvoyée (cinq dans cet exemple) et le SQL dynamique n’est pas une option. Nous avons donc deux possibilités: utiliser beaucoup de IF ... ELSE ... et avoir 10 requêtes, ce qui est un enfer à maintenir, ou ayant une requête comme celle-ci:

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

J'ai essayé la requête plusieurs fois, avec des arguments différents, et ses performances sont plutôt bonnes, mais il semble toujours qu'elle pourrait être optimisée d'une autre manière.

Quelque chose ne va pas avec cette requête ou vous le feriez de cette façon? Proposez-vous une approche différente?

Était-ce utile?

La solution

Simple:

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

Ceci triera sur NULL (DESC) si la commande ASC est sélectionnée, ou inversement.

Laissez la fonction ROW_NUMBER () travailler sur la même expression ORDER BY.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top