Query impaginata utilizzando l'ordinamento su colonne diverse utilizzando ROW_NUMBER () OVER () in SQL Server 2005

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

  •  04-07-2019
  •  | 
  •  

Domanda

Supponiamo che io stia utilizzando il database Northwind e vorrei eseguire una query tramite una procedura memorizzata che contiene, tra gli altri parametri, quanto segue:

  • @Offset per indicare dove inizia l'impaginazione,
  • @Limit per indicare la dimensione della pagina,
  • @SortColumn per indicare la colonna utilizzata ai fini dell'ordinamento,
  • @SortDirection , per indicare l'ordinamento ascendente o discendente.

L'idea è di eseguire l'impaginazione sul database, poiché il set di risultati contiene migliaia di righe, pertanto la memorizzazione nella cache non è un'opzione (e l'utilizzo di VIEWSTATE non è nemmeno considerato come, IMO, fa schifo).

Come forse saprai, SQL Server 2005 fornisce la funzione ROW_NUMBER che restituisce il numero sequenziale di una riga all'interno di una partizione di un set di risultati, a partire da 1 per la prima riga in ciascuna partizione .

Abbiamo bisogno di ordinare su ogni colonna restituita (cinque in questo esempio) e SQL dinamico non è un'opzione, quindi abbiamo due possibilità: usare un sacco di IF ... ELSE ... e avere 10 query, che è un inferno da mantenere, o con una query come la seguente:

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

Ho provato più volte la query, con diversi argomenti, e le sue prestazioni in realtà sono abbastanza buone, ma sembra comunque che potrebbe essere ottimizzato in altro modo.

C'è qualcosa di sbagliato in questa query o lo faresti in questo modo? Proponi un approccio diverso?

È stato utile?

Soluzione

Semplice:

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

Ordinerà su NULL (DESC) se è selezionato l'ordine ASC o viceversa.

Consenti alla funzione ROW_NUMBER () di funzionare sulla stessa espressione ORDER BY.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top