Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Paginacja w SQL Server za pomocą OFFSET/FETCH

Paginacja jest często używana w aplikacjach, w których użytkownik może kliknąć Poprzedni /Dalej aby poruszać się po stronach składających się na wyniki lub kliknij numer strony, aby przejść bezpośrednio do określonej strony.

Uruchamiając zapytania w SQL Server, możesz podzielić wyniki na strony za pomocą OFFSET i FETCH argumenty ORDER BY klauzula. Te argumenty zostały wprowadzone w SQL Server 2012, dlatego możesz użyć tej techniki, jeśli masz SQL Server 2012 lub nowszy.

W tym kontekście podział na strony polega na podzieleniu wyników zapytania na mniejsze porcje, przy czym każda porcja jest kontynuowana w miejscu, w którym zakończyła się poprzednia. Na przykład, jeśli zapytanie zwraca 1000 wierszy, można je podzielić na strony, tak aby były zwracane w grupach po 100. Aplikacja może przekazać numer strony i rozmiar strony do programu SQL Server, a program SQL Server może następnie użyć ich do zwrócenia tylko dane dla żądanej strony.

Przykład 1 – brak stronicowania

Najpierw uruchommy zapytanie, które zwróci wszystkie wiersze w tabeli:

SELECT *
FROM Genres
ORDER BY GenreId;

Wynik:

+-----------+---------+
| GenreId   | Genre   |
|-----------+---------|
| 1         | Rock    |
| 2         | Jazz    |
| 3         | Country |
| 4         | Pop     |
| 5         | Blues   |
| 6         | Hip Hop |
| 7         | Rap     |
| 8         | Punk    |
+-----------+---------+

W tym przykładzie nie zastosowano paginacji – wszystkie wyniki są wyświetlane.

Ten zestaw wyników jest tak mały, że normalnie nie wymagałby stronicowania, ale na potrzeby tego artykułu podzielmy go na strony.

Przykład 2 – Wyświetl pierwsze 3 wyniki

Ten przykład wyświetla pierwsze trzy wyniki:

SELECT *
FROM Genres
ORDER BY GenreId
  OFFSET 0 ROWS
  FETCH NEXT 3 ROWS ONLY;

Wynik:

+-----------+---------+
| GenreId   | Genre   |
|-----------+---------|
| 1         | Rock    |
| 2         | Jazz    |
| 3         | Country |
+-----------+---------+

W takim przypadku określam, że wyniki powinny zaczynać się od pierwszego wyniku i wyświetlać kolejne trzy wiersze. Odbywa się to w następujący sposób:

  • OFFSET 0 ROWS określa, że ​​nie powinno być żadnego przesunięcia (przesunięcie równe zero).
  • FETCH NEXT 3 ROWS ONLY pobiera następne trzy wiersze od przesunięcia. Ponieważ określiłem przesunięcie równe zero, pobierane są pierwsze trzy wiersze.

Gdyby wszystko, czego chcieliśmy, to 3 najlepsze wyniki, moglibyśmy osiągnąć ten sam wynik, używając TOP klauzula zamiast określania wartości przesunięcia i pobrania. Jednak to nie pozwoliłoby nam na wykonanie następnej części.

Przykład 3 – Wyświetl kolejne 3 wyniki

Teraz wyświetlmy kolejne trzy wyniki:

SELECT *
FROM Genres
ORDER BY GenreId
  OFFSET 3 ROWS
  FETCH NEXT 3 ROWS ONLY;

Wynik:

+-----------+---------+
| GenreId   | Genre   |
|-----------+---------|
| 4         | Pop     |
| 5         | Blues   |
| 6         | Hip Hop |
+-----------+---------+

Jedyną rzeczą, którą zmieniłem, było przesunięcie.

Wartości przesunięcia i pobierania mogą być również wyrażeniem dostarczonym jako zmienna, parametr lub stałe podzapytanie skalarne. Gdy używane jest podzapytanie, nie może ono odwoływać się do żadnych kolumn zdefiniowanych w zewnętrznym zakresie zapytania (nie może być skorelowane z zapytaniem zewnętrznym).

Poniższe przykłady używają wyrażeń, aby pokazać dwa podejścia do stronicowania wyników.

Przykład 4 – Podział na strony według numeru wiersza

W tym przykładzie użyto wyrażeń do określenia wiersza numer, od którego należy zacząć.

DECLARE 
  @StartRow int = 1,
  @RowsPerPage int = 3;
  
SELECT *  
FROM Genres
ORDER BY GenreId ASC
    OFFSET @StartRow - 1 ROWS
    FETCH NEXT @RowsPerPage ROWS ONLY;

Wynik:

+-----------+---------+
| GenreId   | Genre   |
|-----------+---------|
| 1         | Rock    |
| 2         | Jazz    |
| 3         | Country |
+-----------+---------+

Tutaj używam @StartRow int = 1 aby określić, że wyniki powinny zaczynać się od pierwszego wiersza.

Oto, co się stanie, jeśli zwiększę tę wartość do 2 .

DECLARE 
  @StartRow int = 2,
  @RowsPerPage int = 3;
  
SELECT *  
FROM Genres
ORDER BY GenreId ASC
    OFFSET @StartRow - 1 ROWS
    FETCH NEXT @RowsPerPage ROWS ONLY;

Wynik:

+-----------+---------+
| GenreId   | Genre   |
|-----------+---------|
| 2         | Jazz    |
| 3         | Country |
| 4         | Pop     |
+-----------+---------+

Zaczyna się w drugim rzędzie. Korzystając z tej metody, mogę określić dokładny wiersz, od którego mam zacząć.

Przykład 5 – Podział na strony według numeru strony

Ten przykład jest prawie identyczny z poprzednim przykładem, z wyjątkiem tego, że pozwala określić numer strony, a nie numer wiersza.

DECLARE 
  @PageNumber int = 1,
  @RowsPerPage int = 3;
  
SELECT *  
FROM Genres
ORDER BY GenreId ASC
    OFFSET (@PageNumber - 1) * @RowsPerPage ROWS
    FETCH NEXT @RowsPerPage ROWS ONLY;

Wynik:

+-----------+---------+
| GenreId   | Genre   |
|-----------+---------|
| 1         | Rock    |
| 2         | Jazz    |
| 3         | Country |
+-----------+---------+

Więc pierwszy wynik jest taki sam. Zobaczmy jednak, co się stanie, gdy zwiększymy @PageNumber do 2 (Zmieniłem nazwę tej zmiennej, aby odzwierciedlić jej nowy cel).

DECLARE 
  @PageNumber int = 2,
  @RowsPerPage int = 3;
  
SELECT *  
FROM Genres
ORDER BY GenreId ASC
    OFFSET (@PageNumber - 1) * @RowsPerPage ROWS
    FETCH NEXT @RowsPerPage ROWS ONLY;

Wynik:

+-----------+---------+
| GenreId   | Genre   |
|-----------+---------|
| 4         | Pop     |
| 5         | Blues   |
| 6         | Hip Hop |
+-----------+---------+

Tym razem wyniki zaczynają się od czwartego rzędu. Używając tej metody, możesz po prostu przekazać numer strony zamiast numeru wiersza.

Przykład 6 – pętla stronicowania

Aby zakończyć, oto krótki przykład, który przechodzi przez wszystkie strony i określa początkowy numer wiersza dla każdej iteracji:

DECLARE 
  @StartRow int = 1, 
  @RowsPerPage int = 3;
WHILE (SELECT COUNT(*) FROM Genres) >= @StartRow  
BEGIN
    SELECT *  
    FROM Genres 
    ORDER BY GenreId ASC   
        OFFSET @StartRow - 1 ROWS   
        FETCH NEXT @RowsPerPage ROWS ONLY;
SET @StartRow = @StartRow + @RowsPerPage;  
CONTINUE
END;

Wynik:

+-----------+---------+
| GenreId   | Genre   |
|-----------+---------|
| 1         | Rock    |
| 2         | Jazz    |
| 3         | Country |
+-----------+---------+
(3 rows affected)
+-----------+---------+
| GenreId   | Genre   |
|-----------+---------|
| 4         | Pop     |
| 5         | Blues   |
| 6         | Hip Hop |
+-----------+---------+
(3 rows affected)
+-----------+---------+
| GenreId   | Genre   |
|-----------+---------|
| 7         | Rap     |
| 8         | Punk    |
+-----------+---------+
(2 rows affected)

Przykład 7 – ROW vs ROWS

Jeśli napotkasz kod, który używa ROW zamiast ROWS , oba argumenty robią to samo. Są synonimami i zapewniają zgodność z ANSI.

Oto pierwszy przykład na tej stronie, ale z ROW zamiast ROWS .

SELECT *
FROM Genres
ORDER BY GenreId
  OFFSET 0 ROW
  FETCH NEXT 3 ROW ONLY;

Wynik:

+-----------+---------+
| GenreId   | Genre   |
|-----------+---------|
| 1         | Rock    |
| 2         | Jazz    |
| 3         | Country |
+-----------+---------+

Przykład 8 – PIERWSZY kontra NASTĘPNY

To samo dotyczy FIRST i NEXT . Są to synonimy zapewniające zgodność z ANSI.

Oto poprzedni przykład, ale z FIRST zamiast NEXT .

SELECT *
FROM Genres
ORDER BY GenreId
  OFFSET 0 ROW
  FETCH FIRST 3 ROW ONLY;

Wynik:

+-----------+---------+
| GenreId   | Genre   |
|-----------+---------|
| 1         | Rock    |
| 2         | Jazz    |
| 3         | Country |
+-----------+---------+

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Wzorzec projektowy dla pól niestandardowych w relacyjnej bazie danych

  2. BULK INSERT z kolumną tożsamości (auto-inkrementacja)

  3. Pięć najważniejszych kwestii dotyczących projektowania indeksu bazy danych w programie SQL Server

  4. Jak znaleźć sortowanie w SQL Server (T-SQL)

  5. Jak utworzyć unikatowe ograniczenie, które również dopuszcza wartości null?