Qual é a melhor maneira de paginar resultados no SQL Server

474

Qual é a melhor maneira (em termos de desempenho) de paginar resultados no SQL Server 2000, 2005, 2008, 2012 se você também deseja obter o número total de resultados (antes de paginar)?

Panagiotis Korros
fonte
26
Eu sempre me perguntei por que eles não suportam apenas a especificação de um deslocamento como parte do TOP (como o suporte ao MySQL / Posgresql com LIMIT / OFFSET). Por exemplo, eles podem ter apenas a sintaxe "SELECT TOP x, y ...." onde x = número de linhas, y = deslocamento inicial. Também seria compatível com versões anteriores.
gregmac 20/09/08
3
hey, eu também ... implementação 2005 paginação de sql é realmente tão desajeitado ...
opensas
6
@gregmac - Sql Server 2012 tem limite / deslocamento agora.
OO
2
A solução aceita não mostra como é a melhor maneira (em termos de desempenho). Algum dado fazendo backup em grandes conjuntos de dados?
OO
3
@OO: Uma boa referência pode ser encontrada aqui: 4guysfromrolla.com/webtech/042606-1.shtml . No entanto, o método de busca superará qualquer paginação baseada em deslocamento.
Lukas Eder

Respostas:

465

Obter o número total de resultados e paginar são duas operações diferentes. Para fins de exemplo, vamos supor que a consulta com a qual você está lidando seja

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

Nesse caso, você determinaria o número total de resultados usando:

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

... o que pode parecer ineficiente, mas na verdade é bastante eficiente, assumindo que todos os índices etc. estejam configurados corretamente.

Em seguida, para obter resultados reais de volta em uma paginação, a seguinte consulta seria mais eficiente:

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

Isso retornará as linhas de 1 a 19 da consulta original. O legal aqui, especialmente para aplicativos da Web, é que você não precisa manter nenhum estado, exceto os números das linhas a serem retornados.

mdb
fonte
37
Apenas a nota que ROW_NUMBER () não existe no SQL Server 2000
John Hunter
6
isso retorna todas as linhas da consulta interna e depois filtra com base na consulta externa? para ex: a consulta interna retorna 100.000 e a consulta externa retorna apenas 20.
SoftwareGeek
2
@ SoftwareGeek: pense nisso como a subconsulta (consulta interna) retornando um fluxo, que é então lido até que a cláusula WHERE externa seja atendida. Como as linhas estão envolvidas com isso depende inteiramente da consulta, mas o otimizador geralmente faz um trabalho muito bom em minimizar esse número. O uso do visualizador de plano de execução gráfica no SQL Server Management Studio (usar Consulta / Incluir Plano de Execução Real) é muito educativo nesse sentido.
Mdb
2
ok, o que se você se dublicated no seleto interior (como quando você tem junção interna) como você usa distinta porque RowNumber é diferente e ele não funciona
user217648
10
A Microsoft adicionou um novo recurso ao SQL 2012 que torna a paginação semelhante ao MySQL. Siga este link para saber como. É um artigo interessante: dbadiaries.com/…
Arash
512

Finalmente, o Microsoft SQL Server 2012 foi lançado, eu realmente gosto de sua simplicidade para uma paginação, você não precisa usar consultas complexas como as respondidas aqui.

Para obter as próximas 10 linhas, basta executar esta consulta:

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- devolvida

Pontos principais a serem considerados ao usá-lo:

  • ORDER BYé obrigatório usar a OFFSET ... FETCHcláusula.
  • OFFSETé obrigatória com FETCH. Você não pode usar ORDER BY ... FETCH.
  • TOPnão pode ser combinado com OFFSETe FETCHna mesma expressão de consulta.
Õzbek
fonte
12
Ainda esperando LISTAGG()/ GROUP_CONCAT().
Bacon Bits
1
@BaconBits Veja esta resposta para uma maneira sorrateira de fazê-lo com FOR XML: stackoverflow.com/a/273330/429949
Richard Marskell - Drackir
1
@ RichardMarskell-Drackir Há muitos problemas com FOR XML PATH (''). Primeiro, ele substitui caracteres de controle XML por códigos de entidade XML. Espero que você não tem <, >ou &em seus dados! Segundo, FOR XML PATH ('')usado dessa maneira é realmente uma sintaxe não documentada. Você deve especificar uma coluna nomeada ou um nome de elemento alternativo. Não fazer nenhum deles não está no documento, o que significa que o comportamento não é confiável. Terceiro, quanto mais aceitamos a FOR XML PATH ('')sintaxe quebrada , menos provável é que o MS realmente forneça uma função real LISTAGG() [ OVER() ] como eles precisavam.
Bacon Bits
4
pena que o perf é tão ruim mssqlgirl.com/…
Jon
5
@ Jon, essa postagem de blog vinculada não é representativa, no sentido de fazer comparações com base no retorno do resultado da página, pesquisando os valores da coluna id.
28928 Noel Abrahams
103

Incrivelmente, nenhuma outra resposta mencionou a maneira mais rápida de fazer a paginação em todas as versões do SQL Server. As compensações podem ser terrivelmente lentas para números de páginas grandes, como é comparado aqui . Existe uma maneira totalmente diferente e muito mais rápida de realizar a paginação no SQL. Isso geralmente é chamado de "método de busca" ou "paginação do conjunto de teclas", conforme descrito nesta postagem do blog aqui .

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

O "predicado de busca"

Os valores @previousScoree @previousPlayerIdsão os respectivos valores do último registro da página anterior. Isso permite que você busque a página "próxima". Se a ORDER BYdireção for ASC, basta usar >.

Com o método acima, você não pode pular imediatamente para a página 4 sem buscar primeiro os 40 registros anteriores. Mas muitas vezes você não quer ir tão longe assim. Em vez disso, você recebe uma consulta muito mais rápida que pode buscar dados em tempo constante, dependendo da indexação. Além disso, suas páginas permanecem "estáveis", independentemente dos dados subjacentes serem alterados (por exemplo, na página 1, enquanto você estiver na página 4).

Essa é a melhor maneira de implementar a paginação ao carregar preguiçosamente mais dados em aplicativos da web, por exemplo.

Observe que o "método de busca" também é chamado de paginação do conjunto de chaves .

Total de registros antes da paginação

A COUNT(*) OVER()função da janela ajudará você a contar o número total de registros "antes da paginação". Se você estiver usando o SQL Server 2000, precisará recorrer a duas consultas para o COUNT(*).

Lukas Eder
fonte
2
@ user960567: Em termos de desempenho, a paginação do conjunto de teclas sempre supera a paginação offset, independentemente de você implementar paginação offset com o padrão SQL OFFSET .. FETCHou com ROW_NUMBER()truques anteriores .
Lukas Eder
21
Eu tenho três problemas com o método de busca. [1] Um usuário não pode pular para a página. [2] assume chaves seqüenciais, ou seja, se alguém excluir algumas 3 linhas, então eu recebo uma página de 7 itens em vez de 10. RowNumberme fornece 10 itens consistentes por página. [3] não funciona com grades existentes que assumem pagenumbere pagesize.
Rebecca
7
@Junto: a paginação do conjunto de teclas não é apropriada para todos os casos. Definitivamente, não é para grades de dados. Mas é perfeito para cenários como rolagem infinita da página de feed do Facebook. Não importa se novas postagens estão sendo adicionadas na parte superior, as postagens subsequentes do feed serão adicionadas corretamente na parte inferior enquanto você estiver rolando a página para baixo. Exemplo de uso perfeito para isso ... Isso seria muito mais difícil de implementar usando o limite de deslocamento / busca usando apenas números.
Robert Koritnik
4
Eu tenho que concordar com o Junto. Este método exclui completamente um cliente que possui uma interface de usuário de paginação bastante padrão de "Anterior 1 2 3 (4) 5 6 Próximo", na qual os usuários podem avançar. Este não é exatamente um caso extremo na minha experiência ...
AaronHS
3
Artigo sobre paginação de conjunto de
teclas
31

A partir do SQL Server 2012, podemos usar OFFSETe FETCH NEXTCláusula para conseguir a paginação.

Tente isso, para o SQL Server:

No SQL Server 2012, um novo recurso foi adicionado à cláusula ORDER BY, para consultar a otimização de um conjunto de dados, facilitando o trabalho com a paginação de dados para quem escreve em T-SQL e para todo o Plano de Execução no SQL Server.

Abaixo do script T-SQL com a mesma lógica usada no exemplo anterior.

--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: Paginando uma consulta com o SQL Server

Mohan
fonte
resposta mais precisa neste julgamento
Vikrant 19/02
17

MSDN: ROW_NUMBER (Transact-SQL)

Retorna o número seqüencial de uma linha dentro de uma partição de um conjunto de resultados, iniciando em 1 para a primeira linha em cada partição.

O exemplo a seguir retorna linhas com os números 50 a 60, inclusive na ordem do 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
Dinesh Rabara
fonte
15

Há uma boa visão geral das diferentes técnicas de paginação em http://www.codeproject.com/KB/aspnet/PagingLarge.aspx

Eu usei o método ROWCOUNT frequentemente com o SQL Server 2000 (também funcionará com 2005 e 2008, apenas avalie o desempenho em comparação com ROW_NUMBER), é extremamente rápido, mas você precisa garantir que as colunas classificadas tenham (principalmente ) valores exclusivos.

liggett78
fonte
1
Curiosamente, esse artigo não menciona o método de busca , que é capaz de realizar paginação em tempo constante ... Ainda um bom artigo
Lukas Eder
6

Para o SQL Server 2000, você pode simular ROW_NUMBER () usando uma variável de tabela com uma coluna IDENTITY:

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

Essa abordagem pode ser estendida para tabelas com chaves de várias colunas e não implica em sobrecarga no desempenho do uso de OR (que ignora o uso do índice). A desvantagem é a quantidade de espaço temporário usado se o conjunto de dados for muito grande e um estiver próximo à última página. Não testei o desempenho do cursor nesse caso, mas pode ser melhor.

Observe que essa abordagem pode ser otimizada para a primeira página de dados. Além disso, ROWCOUNT foi usado, pois o TOP não aceita uma variável no SQL Server 2000.

Thomas S. Trias
fonte
3

A melhor maneira de paginar no sql server 2012 é usando deslocamento e busca a seguir em um procedimento armazenado. Palavra - chave OFFSET - Se usarmos offset com a cláusula order by, a consulta ignorará o número de registros que especificamos nas Linhas OFFSET n.

FETCH NEXT Keywords - Quando usamos o Fetch Next apenas com uma cláusula order by, ele retorna o número de linhas que você deseja exibir na paginação, sem Offset, o SQL gera um erro. Aqui está o exemplo dado abaixo.

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

você pode executá-lo da seguinte maneira.

exec sp_paging 2,3
Debendra Dash
fonte
2

Estas são minhas soluções para paginar o resultado da consulta no lado do servidor SQL. essas abordagens são diferentes entre o SQL Server 2008 e 2012. Além disso, adicionei o conceito de filtragem e ordem com uma coluna. É muito eficiente quando você está paginando, filtrando e solicitando no seu GridView.

Antes do teste, você deve criar uma tabela de amostra e inserir alguma linha nesta tabela: (No mundo real, você deve alterar a cláusula Where considerando os campos da tabela e talvez tenha alguma junção e subconsulta na parte principal do 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

Em todas essas amostras, quero consultar 200 linhas por página e estou buscando a linha para o número de página 1200.

No SQL Server 2008, você pode usar o conceito CTE. Por isso, escrevi dois tipos de consulta para o SQL Server 2008+

- SQL Server 2008 ou superior

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

E segunda solução com CTE no 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 ou superior

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;
Ardalan Shahgholi
fonte
1

Tente esta abordagem:

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

No caso de uso, o seguinte parece ser fácil de usar e rápido. Basta definir o número da página.

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)

também sem 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)
Trovão
fonte
1
O que faz onde 1 = 1 faz senhor?
Errol Paleracio 02/12/19
0

Bem, eu usei o seguinte exemplo de consulta no meu banco de dados SQL 2000, ele funciona bem para o SQL 2005 também. O poder que ele oferece é de ordem dinâmica, usando várias colunas. Eu lhe digo ... isso é poderoso :)

    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

A melhor parte é que sp_executesql armazena em cache chamadas posteriores, desde que você passe os mesmos parâmetros, ou seja, gere o mesmo texto sql.

Jalal El-Shaer
fonte
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

irá reiniciar o idx quando se trata de diferentes init_id

aden
fonte
0

Para a ROW_NUMBERtécnica, se você não tiver uma coluna de classificação para usar, poderá usar o CURRENT_TIMESTAMPseguinte:

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

Isso funcionou bem para mim em pesquisas de tamanhos de tabela de até 700.000.

Isso busca registros de 11 a 30.

user919426
fonte
Como boa prática, com a paginação, você deve tentar ordenar por um conjunto exclusivo de colunas no conjunto de resultados, pois a ordem não deve ser considerada garantida.
Arin Taylor
2
Este vai buscar registros de 11 a 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
salem albadawi
fonte
0

Este bit oferece a capacidade de paginar usando o SQL Server e versões mais recentes do MySQL e carrega o número total de linhas em cada linha. Usa sua chave primária para contar o número de linhas exclusivas.

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
Alex M
fonte
Você pode deixar algum comentário que explique o que seu código faz?
Doug F
0

Esta é uma duplicata da antiga questão da SO de 2012: maneira eficiente de implementar a paginação

DA [TabelaX] ENCOMENDAR PELO [CAMPOX] DESLOCAR 500 LINHAS PESQUISAR APENAS 100 LINHAS

Aqui, o tópico é discutido em maiores detalhes e com abordagens alternativas.

d.popov
fonte
0

A partir de 2012, podemos usar OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY

Damitha
fonte
-19

Você não especificou o idioma nem o driver que está usando. Portanto, estou descrevendo isso de forma abstrata.

  • Crie um conjunto de resultados / dados com rolagem. Isso exigia um primário na (s) mesa (s)
  • pule para o fim
  • solicitar a contagem de linhas
  • pule para o início da página
  • rolar pelas linhas até o final da página
Horcrux7
fonte