Пагинацията често се използва в приложения, където потребителят може да щракне върху Назад /Следващ за да навигирате в страниците, които съставляват резултатите, или щракнете върху номер на страница, за да отидете директно на конкретна страница.
Когато изпълнявате заявки в SQL Server, можете да подредите резултатите на страници, като използвате OFFSET
и FETCH
аргументи на ORDER BY
клауза. Тези аргументи бяха въведени в SQL Server 2012, следователно можете да използвате тази техника, ако имате SQL Server 2012 или по-нова версия.
В този контекст, разделянето на страници е мястото, където разделяте резултатите от заявката на по-малки парчета, като всяка част продължава там, където предишната завърши. Например, ако заявка върне 1000 реда, можете да ги разбиете на страници, така че да бъдат върнати в групи от 100. Едно приложение може да предаде номера на страницата и размера на страницата на SQL Server и SQL Server може след това да го използва, за да върне само данни за заявената страница.
Пример 1 – Без пагинация
Първо, нека изпълним заявка, която връща всички редове в таблица:
SELECT * FROM Genres ORDER BY GenreId;
Резултат:
+-----------+---------+ | GenreId | Genre | |-----------+---------| | 1 | Rock | | 2 | Jazz | | 3 | Country | | 4 | Pop | | 5 | Blues | | 6 | Hip Hop | | 7 | Rap | | 8 | Punk | +-----------+---------+
Този пример не използва пагинация – всички резултати се показват.
Този набор от резултати е толкова малък, че обикновено не изисква пагинация, но за целите на тази статия, нека да го разделим на страници.
Пример 2 – Показване на първите 3 резултата
Този пример показва първите три резултата:
SELECT * FROM Genres ORDER BY GenreId OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY;
Резултат:
+-----------+---------+ | GenreId | Genre | |-----------+---------| | 1 | Rock | | 2 | Jazz | | 3 | Country | +-----------+---------+
В този случай уточнявам, че резултатите трябва да започват от първия резултат и да показват следващите три реда. Това се прави с помощта на следното:
OFFSET 0 ROWS
указва, че не трябва да има отместване (отместване на нула).FETCH NEXT 3 ROWS ONLY
получава следващите три реда от отместването. Тъй като посочих отместване от нула, първите три реда се извличат.
Ако всичко, което искахме, бяха първите 3 резултата, бихме могли да постигнем същия резултат, като използваме TOP
клауза вместо посочване на стойностите за отместване и извличане. Това обаче не би ни позволило да направим следващата част.
Пример 3 – Показване на следващите 3 резултата
Сега нека покажем следващите три резултата:
SELECT * FROM Genres ORDER BY GenreId OFFSET 3 ROWS FETCH NEXT 3 ROWS ONLY;
Резултат:
+-----------+---------+ | GenreId | Genre | |-----------+---------| | 4 | Pop | | 5 | Blues | | 6 | Hip Hop | +-----------+---------+
Така че единственото нещо, което промених, беше изместването.
Стойностите за отместване и извличане могат също да бъдат израз, предоставен като променлива, параметър или константна скаларна подзаявка. Когато се използва подзаявка, тя не може да препраща към колони, дефинирани във външния обхват на заявката (не може да бъде свързан с външната заявка).
Следващите примери използват изрази, за да покажат два подхода за страниране на резултатите.
Пример 4 – Страниране по номер на ред
Този пример използва изрази за определяне на реда номер, от който да започнете.
DECLARE @StartRow int = 1, @RowsPerPage int = 3; SELECT * FROM Genres ORDER BY GenreId ASC OFFSET @StartRow - 1 ROWS FETCH NEXT @RowsPerPage ROWS ONLY;
Резултат:
+-----------+---------+ | GenreId | Genre | |-----------+---------| | 1 | Rock | | 2 | Jazz | | 3 | Country | +-----------+---------+
Тук използвам @StartRow int = 1
за да посочите, че резултатите трябва да започват от първия ред.
Ето какво се случва, ако увелича тази стойност до 2
.
DECLARE @StartRow int = 2, @RowsPerPage int = 3; SELECT * FROM Genres ORDER BY GenreId ASC OFFSET @StartRow - 1 ROWS FETCH NEXT @RowsPerPage ROWS ONLY;
Резултат:
+-----------+---------+ | GenreId | Genre | |-----------+---------| | 2 | Jazz | | 3 | Country | | 4 | Pop | +-----------+---------+
Започва от втория ред. Използвайки този метод, мога да посоча точния ред, от който да започна.
Пример 5 – Страниране по номер на страница
Този пример е почти идентичен с предишния пример, с изключение на това, че ви позволява да посочите номера на страницата, за разлика от номера на реда.
DECLARE @PageNumber int = 1, @RowsPerPage int = 3; SELECT * FROM Genres ORDER BY GenreId ASC OFFSET (@PageNumber - 1) * @RowsPerPage ROWS FETCH NEXT @RowsPerPage ROWS ONLY;
Резултат:
+-----------+---------+ | GenreId | Genre | |-----------+---------| | 1 | Rock | | 2 | Jazz | | 3 | Country | +-----------+---------+
Така че първият резултат е същият. Нека обаче да видим какво се случва, когато увеличим @PageNumber
до 2
(Преименувах тази променлива, за да отразява новата й цел).
DECLARE @PageNumber int = 2, @RowsPerPage int = 3; SELECT * FROM Genres ORDER BY GenreId ASC OFFSET (@PageNumber - 1) * @RowsPerPage ROWS FETCH NEXT @RowsPerPage ROWS ONLY;
Резултат:
+-----------+---------+ | GenreId | Genre | |-----------+---------| | 4 | Pop | | 5 | Blues | | 6 | Hip Hop | +-----------+---------+
Този път резултатите започват от четвъртия ред. Така че, използвайки този метод, можете просто да предадете номера на страницата, а не номера на реда.
Пример 6 – Цикъл на пагинация
За да завършите, ето един бърз пример, който преглежда всички страници и определя номера на началния ред за всяка итерация:
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;
Резултат:
+-----------+---------+ | 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)
Пример 7 – РЕД срещу РЕДОВЕ
Ако срещнете код, който използва ROW
вместо ROWS
, и двата аргумента правят едно и също нещо. Те са синоними и са предоставени за съвместимост с ANSI.
Ето първия пример на тази страница, но с ROW
вместо ROWS
.
SELECT * FROM Genres ORDER BY GenreId OFFSET 0 ROW FETCH NEXT 3 ROW ONLY;
Резултат:
+-----------+---------+ | GenreId | Genre | |-----------+---------| | 1 | Rock | | 2 | Jazz | | 3 | Country | +-----------+---------+
Пример 8 – ПЪРВИ срещу СЛЕДВАЩ
Същото важи и за FIRST
и NEXT
. Това са синоними, предоставени за съвместимост с ANSI.
Ето предишния пример, но с FIRST
вместо NEXT
.
SELECT * FROM Genres ORDER BY GenreId OFFSET 0 ROW FETCH FIRST 3 ROW ONLY;
Резултат:
+-----------+---------+ | GenreId | Genre | |-----------+---------| | 1 | Rock | | 2 | Jazz | | 3 | Country | +-----------+---------+