A paginação é uma técnica fundamental em bancos de dados para recuperar uma parte dos dados em lotes menores e mais gerenciáveis, ao invés de trazer todo o conjunto de uma só vez. Quando usada em conjunto com o SQL Server, a paginação pode melhorar significativamente a performance e a eficiência de suas consultas, reduzindo drasticamente a carga de trabalho do servidor, principalmente quando temos sistemas com muitos usuários ou com um grande volume de dados. Ao dividir a consulta em pedaços menores, o SQL Server pode processar cada parte mais rapidamente e liberar recursos mais rapidamente para outras tarefas.
Além disso, a paginação pode melhorar significativamente a experiência do usuário, tornando a consulta mais rápida e responsiva. Isso é importante em aplicativos da web onde a velocidade e a capacidade de resposta são essenciais. Quando um usuário executa uma consulta em uma página da web, por exemplo, a paginação pode permitir que eles visualizem os resultados em lotes menores, rolando pela lista de dados conforme necessário.
A partir do SQL Server 2012 podemos utilizar as cláusulas OFFSET/FETCH para realizarmos a paginação dos nossos resultados antes de exibir e enviá-los a nossas aplicações. Diante disso, podemos dizer que:
OFFSET: Utilizado para indicar quantas linhas devem ser ignoradas no início do conjunto de resultados.
FETCH: Utilizado para indicar o número máximo de linhas que devem ser retornadas após o OFFSET.
Juntas, essas cláusulas recuperam um subconjunto de linhas do conjunto de resultados, realizando a paginação de resultados. Para demonstrar a sintaxe, vamos pensar no seguinte exemplo. Definimos que queremos recuperar 10 linhas por página e que estamos na página 3, portanto precisamos pular as 20 primeiras linhas da consulta, portanto, utilizamos da seguinte forma:
SELECT coluna1, coluna2
FROM tabela
ORDER BY coluna1
OFFSET 20 ROWS
FETCH NEXT 10 ROWS ONLY
Para poder exemplificar a paginação, nesse artigo utilizaremos uma tabela denominada ‘Pessoa’ que possui 42 registros no total, como podemos ver abaixo:
Utilizando a sintaxe do exemplo que elaboramos, vamos criar algo parametrizável em uma procedure. Para nos auxiliar na manipulação de dados nessa procedure, precisamos criar duas variáveis: número de páginas (@NumberPages) e número de itens por página (@NumberItemPage), que por default, podemos definir 1 página com 20 itens por página.
Se tratando de uma paginação para uma aplicação, precisamos tratar os possíveis erros que possam vir a acontecer, como: cliente solicitar mais páginas que a aplicação possui ou até mesmo solicitar um número negativo de páginas. Para resolver o primeiro problema, podemos criar uma variável que receberá o número máximo de páginas possíveis (@TotalPages). Para determinar o número máximo de páginas, podemos realizar uma simples conta matemática: todos os registros existentes na tabela dividido pelo número de itens por página, ou seja, se temos 42 registros em nossa tabela e queremos por default 20 itens por página, teremos um total de 2,1 páginas. Na resolução do primeiro problema, encontramos outro problema: não é possível mostrarmos 2,1 páginas em uma aplicação, em nosso exemplo, perderíamos 2 registros e só mostraríamos 40. Portanto, precisamos usar a função CEILING do SQL Server, que basicamente arredonda nosso valor para cima para o número inteiro mais próximo, ou seja, ao invés de resultar 2,1 páginas, a divisão resultará 3 páginas. Vale lembrar que devemos transformar os nossos números INT em NUMERIC para a função CEILING funcionar de forma correta, portanto, até agora temos a seguinte sintaxe:
Para resolvermos o segundo problema, é bem simples, toda vez que solicitarem um número negativo ou zero pra aplicação, estipularemos o valor 1 como o mínimo de páginas possível, visto que não existe página negativa ou página zero. E aproveitando o passo que realizamos anteriormente, estipularemos o número máximo de páginas, ou seja, mesmo que a aplicação solicite 80 ou 90 páginas, o resultado sempre será o número máximo armazenado na variável @TotalPages, que no nosso exemplo, é 3 páginas. Diante disso, evoluímos a nossa sintaxe da seguinte forma:
Já esquematizamos toda nossa resolução de possíveis problemas, agora precisamos montar um SELECT com o OFFSET/FETCH que elaboramos lá no início, para trazer a aplicação tudo aquilo que desejamos mostrar em nossa paginação. No exemplo, como estamos tratando da tabela ‘Pessoa’, traremos o ID, Nome, CPF e E-mail de cada um. Antes disso, precisamos nos atentar a um pequeno detalhe: para exibir outras páginas de resultados, precisamos ajustar o valor de OFFSET para que ele comece a partir do início dos resultados para a página desejada e não retorne um resultado extra que já foi exibido na página anterior. Para evitar esse problema, subtrai-se 1 do valor de OFFSET, garantindo que o primeiro resultado da página seja o próximo resultado após o último resultado exibido na página anterior. Se atentando a esse detalhe, evoluímos para a seguinte sintaxe:
Se você acompanhou o meu último artigo, sabe a importância de utilizar a tratativa de erro em uma procedure, entretanto, se ainda não viu, o link se encontra nas referências. Adicionando a tratativa de erro temos a procedure completa para paginação, como mostrada a seguir:
Agora, vamos utilizar a procedure e ver o resultado da nossa paginação. Vale lembrar que utilizaremos o mesmo exemplo discorrido por todo artigo, um valor default de 20 itens por página, que deve mostrar nossos 42 registros em 3 páginas:
Página 1
Página 2
Página 3
A paginação com OFFSET FETCH é considerada mais eficiente e escalável do que a paginação com ROW_NUMBER em grandes conjuntos de dados, pois não requer a classificação de todas as linhas da tabela antes da paginação. Além disso, ela permite que você execute consultas complexas e use cláusulas JOIN, GROUP BY e WHERE em suas consultas de paginação. Embora a paginação com ROW_NUMBER seja mais simples, ela pode ser menos flexível em termos de consulta de dados. É importante lembrar que a cláusula ORDER BY deve estar presente na consulta ao usar OFFSET FETCH para garantir resultados consistentes em diferentes páginas.
REFERÊNCIAS
Cláusula ORDER BY (Transact-SQL) - SQL Server | Microsoft Learn
CEILING (Transact-SQL) - SQL Server | Microsoft Learn
Tratativa de erros no SQL Server com TRY/CATCH | Site Dataside 2023