2008-10-23 10 views
7

Załóżmy, że używam bazy danych Northwind i chciałbym uruchomić kwerendę za pomocą procedury przechowywanej, która zawiera, oprócz innych parametrów, następujące:Pośrednia kwerenda za pomocą sortowania na różnych kolumnach za pomocą ROW_NUMBER() OVER() w SQL Server 2005

  • @Offset wskazać gdzie zaczyna się paginacja,
  • @Limit wskazać rozmiar strony,
  • @SortColumn wskazać kolumnę używaną do celów sortowania,
  • @SortDirection, aby wskazać sortowanie rosnąco lub potomka.

Chodzi o to, aby zrobić podział na strony w bazie danych, ponieważ zestaw wyników zawiera tysiące wierszy, więc buforowanie nie jest opcją (a korzystanie z funkcji VIEWSTATE nie jest nawet uważane za IMO, zasysane).

Jak wiadomo SQL Server 2005 udostępnia funkcję ROW_NUMBER który zwraca kolejny numer wiersza w rozbicie zbioru wynikowego, zaczynając od 1 dla pierwszego wiersza w każdej partycji.

Potrzebujemy sortowania na każdej zwróconej kolumnie (pięć w tym przykładzie), a dynamiczny SQL nie jest opcją, więc mamy dwie możliwości: używanie dużej liczby IF ... ELSE ... i posiadanie 10 zapytań, co jest piekłem do utrzymania lub posiadania kwerendy tak:

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 

próbowałem zapytaniu kilka razy, z różnymi argumentami, a jego wydajność jest całkiem dobry faktycznie, ale to uspokaja wygląda to może być zoptymalizowane w jakiś inny sposób.

Czy coś jest nie tak z tym zapytaniem, czy zrobiłbyś to w ten sposób? Czy proponujesz inne podejście?

+0

Mój głupi błąd, naprawiłem to. Proszę spróbuj ponownie. – Tomalak

Odpowiedz

6

prosty:

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 

to sortuje na NULL (wzorzec) jeżeli kolejność ASC wybrany lub vice versa.

Niech funkcja ROW_NUMBER() działa nad tym samym wyrażeniem ORDER BY.

+0

Dziś po południu spróbuję użyć tej funkcji z terminarzem zapytań i profilerem, aby porównać wyniki :) –

+0

Chętnie zobaczę wyniki. :-) – Tomalak

+0

Dokładnie ten podaje następujący błąd: 'Msg 207, Level 16, State 1, Line 41 - Niepoprawna nazwa kolumny 'RowNumber'.'. Użyję wtedy klauzuli WITH. –