Jaki jest najlepszy sposób stronicowania wyników w SQL Server


474

Jaki jest najlepszy (pod względem wydajności) sposób stronicowania wyników w SQL Server 2000, 2005, 2008, 2012, jeśli chcesz również uzyskać całkowitą liczbę wyników (przed paginowaniem)?


26
Zawsze zastanawiałem się, dlaczego nie obsługują tylko określenia przesunięcia w ramach TOP (jak obsługa MySQL / Posgresql z LIMIT / OFFSET). Np. Mogą mieć po prostu składnię „SELECT TOP x, y ....” gdzie x = liczba wierszy, y = początkowe przesunięcie. Byłby również kompatybilny wstecz.
gregmac

3
hej, ja też ... implementacja paginacji w 2005 r. sql jest naprawdę bardzo akward ...
otwiera

6
@gregmac - Sql Server 2012 ma teraz limit / offset.
OO

2
Przyjęte rozwiązanie nie pokazuje, w jaki sposób jest to najlepszy sposób (pod względem wydajności). Jakieś kopie zapasowe na dużych zestawach danych?
OO

3
@OO: Dobry test porównawczy można znaleźć tutaj: 4guysfromrolla.com/webtech/042606-1.shtml . Jednak metoda wyszukiwania przewyższy każdą paginację opartą na przesunięciu.
Lukas Eder

Odpowiedzi:


465

Uzyskiwanie całkowitej liczby wyników i dzielenie na strony to dwie różne operacje. Na potrzeby tego przykładu załóżmy, że zapytanie, którym się zajmujesz, jest

SELECT * FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate

W takim przypadku możesz określić całkowitą liczbę wyników, używając:

SELECT COUNT(*) FROM Orders WHERE OrderDate >= '1980-01-01'

... co może wydawać się nieefektywne, ale w rzeczywistości jest dość wydajne, zakładając, że wszystkie indeksy itp. są poprawnie skonfigurowane.

Następnie, aby uzyskać rzeczywiste wyniki z powrotem w sposób stronicowany, najbardziej wydajne byłoby następujące zapytanie:

SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum, *
          FROM      Orders
          WHERE     OrderDate >= '1980-01-01'
        ) AS RowConstrainedResult
WHERE   RowNum >= 1
    AND RowNum < 20
ORDER BY RowNum

Zwróci to wiersze 1-19 pierwotnego zapytania. Fajną rzeczą, szczególnie w przypadku aplikacji internetowych, jest to, że nie musisz utrzymywać żadnego stanu, z wyjątkiem numerów wierszy, które mają zostać zwrócone.


37
Wystarczy zauważyć, że ROW_NUMBER () nie istnieje w SQL Server 2000
John Hunter

6
czy to zwraca wszystkie wiersze z zapytania wewnętrznego, a następnie filtruje na podstawie zapytania zewnętrznego? na przykład: zapytanie wewnętrzne zwraca 100 000, a zapytanie zewnętrzne zwraca tylko 20.
SoftwareGeek

2
@ SoftwareGeek: pomyśl o tym jako o podzapytaniu (wewnętrznym zapytaniu) zwracającym strumień, który jest następnie odczytywany aż do spełnienia zewnętrznej klauzuli WHERE. To, w jaki sposób wiersze mogą być w to zaangażowane, zależy całkowicie od zapytania, ale optymalizator zazwyczaj wykonuje bardzo dobrą robotę, minimalizując tę ​​liczbę. Korzystanie z przeglądarki graficznego planu wykonania w SQL Server Management Studio (użyj zapytania / Uwzględnij rzeczywisty plan wykonania) jest pod tym względem bardzo pouczające.
mdb

2
ok, a co jeśli zostaniesz zdublowany w wewnętrznej selekcji (np. kiedy masz wewnętrzne sprzężenie), jak używasz odrębnego, ponieważ RowNumber jest inny i nie działa
użytkownik217648

10
Microsoft dodał nową funkcję do SQL 2012, która upodabnia paginację do MySQL. Kliknij ten link, aby dowiedzieć się, jak to zrobić. To ciekawy artykuł: dbadiaries.com/…
Arash,

511

Wreszcie Microsoft SQL Server 2012 został wydany, bardzo podoba mi się jego prostota w przypadku paginacji, nie musisz używać skomplikowanych zapytań, takich jak odpowiedzi tutaj.

Aby uzyskać kolejne 10 wierszy, wystarczy uruchomić to zapytanie:

SELECT * FROM TableName ORDER BY id OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;

https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql#using-offset-and-fetch-to-limit-the-rows- zwrócony

Najważniejsze kwestie, które należy wziąć pod uwagę podczas jego używania:

  • ORDER BYjest obowiązkowe, aby użyć OFFSET ... FETCHklauzuli.
  • OFFSETklauzula jest obowiązkowa z FETCH. Nie możesz użyćORDER BY ... FETCH .
  • TOPnie może być połączony z OFFSETi FETCHw tym samym ekspresję zapytania.

12
Wciąż czekam na LISTAGG()/ GROUP_CONCAT().
Bacon Bits

1
@BaconBits Spójrz na tę odpowiedź w sprytny sposób FOR XML: stackoverflow.com/a/273330/429949
Richard Marskell - Drackir

1
@ RichardMarskell-Drackir Jest wiele problemów FOR XML PATH (''). Po pierwsze, zastępuje znaki sterujące XML kodami encji XML. Nadzieję, że nie masz <, >albo &w danych! Po drugie, FOR XML PATH ('')w ten sposób używana jest nieudokumentowana składnia. Musisz podać nazwaną kolumnę lub alternatywną nazwę elementu. Nie robienie tego nie jest w dokumencie, co oznacza, że ​​zachowanie jest zawodne. Po trzecie, im bardziej akceptujemy zepsutą FOR XML PATH ('')składnię, tym mniej prawdopodobne jest, że stwardnienie rozsiane zapewnia rzeczywistą LISTAGG() [ OVER() ] funkcję taką, jaką musieli.
Bacon Bits

4
szkoda, że ​​perf jest taki zły mssqlgirl.com/…
Jon

5
@Jon, ten link do blogu nie jest reprezentatywny, w tym sensie, że dokonuje porównań na podstawie zwrócenia wyniku strony przez wyszukiwanie wartości kolumny id.
Noel Abrahams,

103

Niesamowicie, żadna inna odpowiedź nie wspomina o najszybszym sposobie podziału na strony we wszystkich wersjach SQL Server. Przesunięcia mogą być strasznie powolne w przypadku dużych numerów stron, co jest tutaj testowane . Istnieje całkowicie inny, znacznie szybszy sposób wykonywania paginacji w SQL. Jest to często nazywane „metodą wyszukiwania” lub „paginacją zestawu kluczy”, jak opisano w tym poście na blogu tutaj .

SELECT TOP 10 first_name, last_name, score, COUNT(*) OVER()
FROM players
WHERE (score < @previousScore)
   OR (score = @previousScore AND player_id < @previousPlayerId)
ORDER BY score DESC, player_id DESC

„Predicate seek”

Wartości @previousScorei @previousPlayerIdsą odpowiednimi wartościami ostatniego rekordu z poprzedniej strony. Pozwala to pobrać „następną” stronę. Jeśli ORDER BYkierunek jest ASC, po prostu użyj> zamiast tego.

Przy użyciu powyższej metody nie można od razu przejść do strony 4 bez uprzedniego pobrania 40 poprzednich rekordów. Ale często i tak nie chcesz skakać tak daleko. Zamiast tego otrzymujesz znacznie szybsze zapytanie, które może być w stanie pobrać dane w stałym czasie, w zależności od twojego indeksowania. Ponadto strony pozostają „stabilne”, bez względu na to, czy zmieniają się dane bazowe (np. Na stronie 1, gdy jesteś na stronie 4).

Jest to najlepszy sposób na wdrożenie paginacji, gdy na przykład leniwe ładowanie większej ilości danych w aplikacjach internetowych.

Uwaga: „metoda wyszukiwania” nazywana jest również paginacją zestawu kluczy .

Suma rekordów przed paginacją

Funkcja COUNT(*) OVER()okna pomoże ci policzyć całkowitą liczbę rekordów „przed paginacją”. Jeśli używasz programu SQL Server 2000, będziesz musiał skorzystać z dwóch zapytań dotyczących COUNT(*).


2
@ user960567: Jeśli chodzi o wydajność, stronicowanie zestawu kluczy zawsze pokonuje stronicowanie z przesunięciem, bez względu na to, czy implementuje się stronicowanie z przesunięciem w standardzie SQL OFFSET .. FETCH, czy w poprzednich ROW_NUMBER()sztuczkach.
Lukas Eder

21
Mam trzy problemy z metodą wyszukiwania. [1] Użytkownik nie może przejść do strony. [2] zakłada sekwencyjne klucze, tj. Jeśli ktoś usunie jakieś 3 wiersze, wtedy dostanę stronę 7 pozycji zamiast 10. RowNumberdaje mi spójne 10 pozycji na stronie. [3] nie działa z istniejącymi siatkami, które zakładają pagenumberi pagesize.
Rebecca

7
@Junto: stronicowanie zestawu kluczy nie jest odpowiednie dla wszystkich przypadków. To zdecydowanie nie jest dla siatek danych. Ale jest idealny do scenariuszy takich jak nieskończone przewijanie strony kanału na Facebooku. Nie ma znaczenia, czy nowe posty są dodawane u góry, kolejne posty będą poprawnie dodawane na dole podczas przewijania w dół. Idealny przykład użycia tego ... Takie rzeczy byłoby znacznie trudniejsze do wdrożenia przy użyciu limitu przesunięcia / pobierania tylko przy użyciu liczb.
Robert Koritnik

4
Muszę się zgodzić z Junto. Ta metoda całkowicie wyklucza klienta, który ma dość standardowy interfejs stronicowania „Wstecz 1 2 3 (4) 5 6 Dalej”, w którym użytkownicy mogą skoczyć do przodu. Z mojego doświadczenia nie jest to przypadek
skrajny

3
Artykuł na temat paginacji zestawu kluczy tutaj
Stphane

31

Od SQL Server 2012 możemy użyć OFFSETi FETCH NEXTClause, aby osiągnąć podział na strony.

Wypróbuj to, dla SQL Server:

W SQL Server 2012 została dodana nowa funkcja w klauzuli ORDER BY, służąca do kwerendy optymalizacji zestawu danych, ułatwiająca pracę ze stronicowaniem danych dla każdego, kto pisze w T-SQL, a także dla całego planu wykonania w SQL Server.

Poniżej skryptu T-SQL z taką samą logiką, jak w poprzednim przykładzie.

--CREATING A PAGING WITH OFFSET and FETCH clauses IN "SQL SERVER 2012"
DECLARE @PageNumber AS INT, @RowspPage AS INT
SET @PageNumber = 2
SET @RowspPage = 10 
SELECT ID_EXAMPLE, NM_EXAMPLE, DT_CREATE
FROM TB_EXAMPLE
ORDER BY ID_EXAMPLE
OFFSET ((@PageNumber - 1) * @RowspPage) ROWS
FETCH NEXT @RowspPage ROWS ONLY;

TechNet: Stronicowanie zapytania za pomocą programu SQL Server


najdokładniejsza odpowiedź w tej próbie
Vikrant

17

MSDN: ROW_NUMBER (Transact-SQL)

Zwraca kolejny numer wiersza w obrębie partycji zestawu wyników, zaczynając od 1 dla pierwszego wiersza w każdej partycji.

Poniższy przykład zwraca wiersze o numerach od 50 do 60 włącznie w kolejności OrderDate.

WITH OrderedOrders AS
(
    SELECT
        ROW_NUMBER() OVER(ORDER BY FirstName DESC) AS RowNumber, 
        FirstName, LastName, ROUND(SalesYTD,2,1) AS "Sales YTD"
    FROM [dbo].[vSalesPerson]
) 
SELECT RowNumber, 
    FirstName, LastName, Sales YTD 
FROM OrderedOrders 
WHERE RowNumber > 50 AND RowNumber < 60;
  RowNumber FirstName    LastName               SalesYTD
  --- -----------  ---------------------- -----------------
  1   Linda        Mitchell               4251368.54
  2   Jae          Pak                    4116871.22
  3   Michael      Blythe                 3763178.17
  4   Jillian      Carson                 3189418.36
  5   Ranjit       Varkey Chudukatil      3121616.32
  6   José         Saraiva                2604540.71
  7   Shu          Ito                    2458535.61
  8   Tsvi         Reiter                 2315185.61
  9   Rachel       Valdez                 1827066.71
  10  Tete         Mensa-Annan            1576562.19
  11  David        Campbell               1573012.93
  12  Garrett      Vargas                 1453719.46
  13  Lynn         Tsoflias               1421810.92
  14  Pamela       Ansman-Wolfe           1352577.13

15

Dobry przegląd różnych technik stronicowania znajduje się na stronie http://www.codeproject.com/KB/aspnet/PagingLarge.aspx

Stosowałem metodę ROWCOUNT dość często głównie z SQL Server 2000 (będzie również działać w 2005 i 2008 roku, po prostu mierz wydajność w porównaniu do ROW_NUMBER), jest błyskawiczny, ale musisz się upewnić, że posortowane kolumny mają (głównie ) unikalne wartości.


1
Co ciekawe, w tym artykule nie wspomina się o metodzie wyszukiwania , która jest w stanie wykonywać stronicowanie w stałym czasie ... Nadal dobry artykuł
Lukas Eder,

6

W przypadku SQL Server 2000 można symulować ROW_NUMBER () przy użyciu zmiennej tabeli z kolumną TOŻSAMOŚĆ:

DECLARE @pageNo int -- 1 based
DECLARE @pageSize int
SET @pageNo = 51
SET @pageSize = 20

DECLARE @firstRecord int
DECLARE @lastRecord int
SET @firstRecord = (@pageNo - 1) * @pageSize + 1 -- 1001
SET @lastRecord = @firstRecord + @pageSize - 1   -- 1020

DECLARE @orderedKeys TABLE (
  rownum int IDENTITY NOT NULL PRIMARY KEY CLUSTERED,
  TableKey int NOT NULL
)

SET ROWCOUNT @lastRecord
INSERT INTO @orderedKeys (TableKey) SELECT ID FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate

SET ROWCOUNT 0

SELECT t.*
FROM Orders t
  INNER JOIN @orderedKeys o ON o.TableKey = t.ID
WHERE o.rownum >= @firstRecord
ORDER BY o.rownum

Podejście to można rozszerzyć na tabele z kluczami wielokolumnowymi i nie wiąże się to z dodatkowym obciążeniem związanym z używaniem OR (co pomija użycie indeksu). Minusem jest ilość tymczasowego miejsca zużytego, jeśli zestaw danych jest bardzo duży i jeden znajduje się w pobliżu ostatniej strony. W tym przypadku nie testowałem wydajności kursora, ale może być lepiej.

Pamiętaj, że to podejście można zoptymalizować dla pierwszej strony danych. Ponadto zastosowano ROWCOUNT, ponieważ TOP nie akceptuje zmiennej w SQL Server 2000.


3

Najlepszym sposobem stronicowania na serwerze SQL 2012 jest użycie przesunięcia i pobierania dalej w procedurze składowanej. Słowo kluczowe PRZESUNIĘCIE - Jeśli użyjemy przesunięcia względem kolejności według klauzuli, wówczas zapytanie pominie liczbę rekordów określoną w PRZESUNIĘCIE n Wiersze.

FETCH NEXT Słowa kluczowe - gdy używamy Fetch Next tylko z klauzulą ​​porządkującą, zwraca tylko liczbę wierszy, które chcesz wyświetlić w stronicowaniu, bez przesunięcia, wówczas SQL wygeneruje błąd. oto przykład podany poniżej.

create procedure sp_paging
(
 @pageno as int,
 @records as int
)
as
begin
declare @offsetcount as int
set @offsetcount=(@pageno-1)*@records
select id,bs,variable from salary order by id offset @offsetcount rows fetch Next @records rows only
end

możesz wykonać to w następujący sposób.

exec sp_paging 2,3

2

To są moje rozwiązania do stronicowania wyniku zapytania po stronie serwera SQL. te podejścia są różne w SQL Server 2008 i 2012. Ponadto dodałem koncepcję filtrowania i porządkowania według jednej kolumny. Jest bardzo wydajny, gdy stronicujesz, filtrujesz i porządkujesz w swoim Gridview.

Przed testowaniem musisz utworzyć jedną przykładową tabelę i wstawić wiersz w tej tabeli: (W prawdziwym świecie musisz zmienić klauzulę Where, biorąc pod uwagę pola tabeli i być może masz trochę sprzężenia i podzapytania w głównej części select)

Create Table VLT
(
    ID int IDentity(1,1),
    Name nvarchar(50),
    Tel Varchar(20)
)
GO


Insert INTO VLT
VALUES
    ('NAME' + Convert(varchar(10),@@identity),'FAMIL' + Convert(varchar(10),@@identity))
GO 500000

We wszystkich tych przykładach chcę wykonać zapytanie do 200 wierszy na stronę i pobieram wiersz dla strony o numerze 1200.

W SQL Server 2008 możesz użyć koncepcji CTE. Z tego powodu napisałem dwa typy zapytań dla SQL Server 2008+

- SQL Server 2008+

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM
  (  
    SELECT 
      ROW_NUMBER() 
        OVER( ORDER BY 
                CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
                      THEN VLT.ID END ASC,
                CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
                      THEN VLT.ID END DESC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
                      THEN VLT.Tel END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
                      THEN VLT.Tel END ASC
         ) AS RowNum
      ,*  
    FROM VLT 
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1   
  ) AS Data
WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
  AND Data.RowNum <= @PageSize * @PageNumber
ORDER BY Data.RowNum

GO

I drugie rozwiązanie z CTE w SQL Server 2008+

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

;WITH
  Data_CTE
  AS
  (  
    SELECT 
      ROW_NUMBER() 
        OVER( ORDER BY 
                CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
                      THEN VLT.ID END ASC,
                CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
                      THEN VLT.ID END DESC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
                      THEN VLT.Tel END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
                      THEN VLT.Tel END ASC
         ) AS RowNum
      ,*  
    FROM VLT
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1     
  )

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM Data_CTE AS Data
WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
  AND Data.RowNum <= @PageSize * @PageNumber
ORDER BY Data.RowNum

- SQL Server 2012+

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

;WITH
  Data_CTE
  AS
  (  
    SELECT 
      *  
    FROM VLT
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1         
  )

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM Data_CTE AS Data
ORDER BY 
    CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
        THEN Data.ID END ASC,
    CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
        THEN Data.ID END DESC,
    CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
        THEN Data.Name END ASC,
    CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
        THEN Data.Name END ASC,
    CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
        THEN Data.Tel END ASC,
    CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
        THEN Data.Tel END ASC
OFFSET @PageSize * (@PageNumber - 1) ROWS FETCH NEXT @PageSize ROWS ONLY;

1

Wypróbuj to podejście:

SELECT TOP @offset a.*
FROM (select top @limit b.*, COUNT(*) OVER() totalrows 
        from TABLENAME b order by id asc) a
ORDER BY id desc;

1

Przypadki użycia wydają się łatwe w użyciu i szybkie. Wystarczy ustawić numer strony.

use AdventureWorks
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 6;
with result as(
SELECT SalesOrderDetailID, SalesOrderID, ProductID,
ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS RowNum
FROM Sales.SalesOrderDetail
where 1=1
)
select SalesOrderDetailID, SalesOrderID, ProductID from result
WHERE result.RowNum BETWEEN ((@PageNumber-1)*@RowsPerPage)+1
AND @RowsPerPage*(@PageNumber)

także bez CTE

use AdventureWorks
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 6
SELECT SalesOrderDetailID, SalesOrderID, ProductID
FROM (
SELECT SalesOrderDetailID, SalesOrderID, ProductID,
ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS RowNum
FROM Sales.SalesOrderDetail
where 1=1
 ) AS SOD
WHERE SOD.RowNum BETWEEN ((@PageNumber-1)*@RowsPerPage)+1
AND @RowsPerPage*(@PageNumber)

1
Co robi, gdy 1 = 1, proszę pana?
Errol Paleracio,

0

Cóż, użyłem następującego przykładowego zapytania w mojej bazie danych SQL 2000, działa również dobrze dla SQL 2005. Moc, którą daje, jest dynamicznie uporządkowana za pomocą wielu kolumn. Mówię ci ... to jest potężne :)

    ALTER PROCEDURE [dbo].[RE_ListingReports_SelectSummary] 

@CompanyID  int,
@pageNumber     int,
@pageSize   int, 
@sort       varchar(200)
AS

DECLARE @sql nvarchar(4000)
DECLARE @strPageSize nvarchar(20)
DECLARE @strSkippedRows nvarchar(20)
DECLARE @strFields nvarchar(4000)
DECLARE @strFilter nvarchar(4000)
DECLARE @sortBy nvarchar(4000)
DECLARE @strFrom nvarchar(4000)
DECLARE @strID nvarchar(100)

If(@pageNumber < 0)
  SET @pageNumber = 1
SET @strPageSize = CAST(@pageSize AS varchar(20)) 
SET @strSkippedRows = CAST(((@pageNumber - 1) * @pageSize) AS varchar(20))-- For    example if pageNumber is 5  pageSize is 10, then SkippedRows = 40.
SET @strID = 'ListingDbID'
SET @strFields = 'ListingDbID,
ListingID,  
[ExtraRoom]
'
SET @strFrom = ' vwListingSummary '

SET @strFilter = ' WHERE
        CompanyID = ' + CAST(@CompanyID As varchar(20)) 
End
SET @sortBy = ''
if(len(ltrim(rtrim(@sort))) > 0)
SET @sortBy = ' Order By ' + @sort

-- Total Rows Count

SET @sql =  'SELECT Count(' + @strID + ')  FROM ' + @strFROM + @strFilter
EXEC sp_executesql @sql

--// This technique is used in a Single Table pagination
SET @sql = 'SELECT ' + @strFields + ' FROM ' + @strFROM +
    ' WHERE ' + @strID +  ' IN ' + 
   '  (SELECT TOP ' + @strPageSize + ' ' + @strID + ' FROM ' + @strFROM + @strFilter + 
             ' AND  ' + @strID + ' NOT IN ' + '
          (SELECT TOP ' + @strSkippedRows + ' ' + @strID + ' FROM ' + @strFROM + @strFilter + @SortBy + ') ' 
   + @SortBy + ') ' + @SortBy
Print @sql 
EXEC sp_executesql @sql

Najlepszą częścią jest sp_executesql buforuje późniejsze wywołania, pod warunkiem, że przekazujesz te same parametry, tj. Generujesz ten sam tekst sql.


0
   CREATE view vw_sppb_part_listsource as 
    select row_number() over (partition by sppb_part.init_id order by sppb_part.sppb_part_id asc ) as idx, * from (
      select 
          part.SPPB_PART_ID
          , 0 as is_rev
          , part.part_number 
          , part.init_id 
      from t_sppb_init_part part 
      left join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
      where prev.SPPB_PART_ID is null 
      union 
      select 
          part.SPPB_PART_ID
          , 1 as is_rev
          , prev.part_number 
          , part.init_id 
      from t_sppb_init_part part 
      inner join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
    ) sppb_part

zrestartuje idx, jeśli chodzi o inny init_id


0

W przypadku tej ROW_NUMBERtechniki, jeśli nie masz kolumny do sortowania, możesz użyć CURRENT_TIMESTAMPnastępujących opcji:

SELECT TOP 20 
    col1,
    col2,
    col3,
    col4
FROM (
    SELECT 
         tbl.col1 AS col1
        ,tbl.col2 AS col2
        ,tbl.col3 AS col3
        ,tbl.col4 AS col4
        ,ROW_NUMBER() OVER (
            ORDER BY CURRENT_TIMESTAMP
            ) AS sort_row
    FROM dbo.MyTable tbl
    ) AS query
WHERE query.sort_row > 10
ORDER BY query.sort_row

Działa to dla mnie dobrze w przypadku wyszukiwań powyżej rozmiarów tabel nawet do 700 000.

Pobiera rekordy od 11 do 30.


Jako dobrą praktykę, w przypadku stronicowania należy spróbować uporządkować według unikalnego zestawu kolumn w zestawie wyników, ponieważ nie należy traktować kolejności jako gwarantowanej.
Arin Taylor,

2
Pobiera rekordy od 11 do 30.
Ardalan Shahgholi

0
create PROCEDURE SP_Company_List (@pagesize int = -1 ,@pageindex int= 0   ) > AS BEGIN  SET NOCOUNT ON;


    select  Id , NameEn     from Company  ORDER by Id ASC  
OFFSET (@pageindex-1 )* @pagesize   ROWS FETCH NEXt @pagesize ROWS ONLY END  GO

DECLARE   @return_value int

EXEC  @return_value = [dbo].[SP_Company_List]         @pagesize = 1 ,         > @pageindex = 2

SELECT    'Return Value' = @return_value

GO

0

Ten bit daje możliwość stronicowania przy użyciu SQL Server i nowszych wersji MySQL i przenosi całkowitą liczbę wierszy w każdym wierszu. Używa klucza podstawowego, aby policzyć liczbę unikalnych wierszy.

WITH T AS
(  
  SELECT TABLE_ID, ROW_NUMBER() OVER (ORDER BY TABLE_ID) AS RN
  , (SELECT COUNT(TABLE_ID) FROM TABLE) AS TOTAL 
  FROM TABLE (NOLOCK)
)

SELECT T2.FIELD1, T2.FIELD2, T2.FIELD3, T.TOTAL 
FROM TABLE T2 (NOLOCK)
INNER JOIN T ON T2.TABLE_ID=T.TABLE_ID
WHERE T.RN >= 100
AND T.RN < 200

Czy możesz zostawić komentarze wyjaśniające, co robi Twój kod?
Doug F


0

Od 2012 roku możemy korzystać OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY


-19

Nie określono języka ani używanego sterownika. Dlatego opisuję to abstrakcyjnie.

  • Utwórz przewijalny zestaw wyników / zestaw danych. Wymagało to podstawowej w tabelach
  • przeskoczyć na koniec
  • zażądać liczby wierszy
  • przeskocz na początek strony
  • przewijaj rzędy aż do końca strony
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.