Paginação no SQL Server

17

Eu tenho um banco de dados muito grande, aproximadamente 100 GB. Estou executando a consulta:

select * from <table_name>;

e quero mostrar apenas as linhas 100 a 200.

Eu quero entender como isso acontece internamente. O banco de dados busca todos os registros do disco na memória e envia de 100 a 400 linhas para o cliente consultante? Ou existe algum mecanismo, para que apenas esses registros (100 a 200) sejam buscados no banco de dados - usando o mecanismo de indexação como árvores B, etc.?

Descobri que isso está relacionado ao conceito de paginação, mas não consegui exatamente descobrir como isso acontece internamente no nível do banco de dados.

AV94
fonte

Respostas:

37

Na consulta que você postou:

select * from <table_name>;

As linhas 100 a 200 não existem, porque você não especifica um ORDER BY. O pedido não é garantido, a menos que você inclua o ORDER BY por várias razões interessantes, mas esse não é realmente o ponto aqui.

Então, para ilustrar seu argumento, vamos usar uma tabela - vou usar a tabela Users do despejo de dados Stack Overflow e executar esta consulta:

SELECT * FROM dbo.Users ORDER BY DisplayName;

Por padrão, não há índice no campo DisplayName, portanto, o SQL Server precisa varrer a tabela inteira e classificá-la por DisplayName. Aqui está o plano de execução :

Varredura de índice em cluster com uma classificação

Não é bonito - é muito trabalho, com um custo estimado da subárvore em torno de 30k. (Você pode vê-lo passando o mouse sobre o operador de seleção em PasteThePlan.) Então, o que acontece se queremos apenas as linhas 100-200? Podemos usar esta sintaxe no SQL Server 2012+:

SELECT * FROM dbo.Users ORDER BY DisplayName OFFSET 100 ROWS FETCH NEXT 100 ROWS ONLY;

O plano de execução também é bastante feio:

Verificação de índice em cluster com uma classificação e uma parte superior

O SQL Server ainda está varrendo a tabela inteira para criar a lista classificada, apenas para fornecer suas linhas de 100 a 200, e o custo ainda é de cerca de 30 mil. Pior ainda, toda essa lista será reconstruída toda vez que sua consulta for executada (porque, afinal, alguém pode ter alterado seu DisplayName.)

Para acelerar, podemos criar um índice não clusterizado em DisplayName, que é uma cópia da nossa tabela, classificada por esse campo específico:

CREATE INDEX IX_DisplayName ON dbo.Users(DisplayName);

Com esse índice, o plano de execução da nossa consulta agora busca um índice:

Pesquisa de índice e pesquisa de chave

A consulta termina instantaneamente e tem um custo estimado da subárvore de apenas 0,66 (em oposição a 30k).

Em resumo, se você organizar os dados de uma maneira que ofereça suporte às consultas que você executa com freqüência, sim, o SQL Server poderá usar atalhos para acelerar suas consultas. Se, por outro lado, tudo que você tem são pilhas ou índices agrupados, você está ferrado.

Brent Ozar
fonte
"Por padrão, não há índice no campo DisplayName, portanto, o SQL Server precisa varrer a tabela inteira e classificá-la por DisplayName." Perdoe-me se essa é uma pergunta muito básica - no caso, citei sua resposta: quando você disse "Analisar a tabela inteira", isso significa que todos os dados são trazidos para a memória e classificados (o que não parece da maneira certa)?
AV94 4/16
Pela sua resposta, entendo que, se o campo estiver indexado, fazer consultas como - obter a 100a a 200a linha é muito eficiente, pois o SQL procura no índice (árvore B etc) e vai diretamente para esse ponto (100a linha). Poderia, por favor, me dizer se esse é o entendimento correto?
AV94 4/16
@AnilVedala sobre sua primeira pergunta - sim, os dados devem ser classificados. De que outra forma um banco de dados poderia fazer isso com uma lista não classificada?
Brent Ozar
1
@AnilVedala sobre sua segunda pergunta - é aí que entra o último plano de execução que eu dei. (Se você está perguntando sobre como ler um plano de execução, pegue o livro Planos de Execução de Grant Fritchey.)
Brent Ozar
15

Assim como uma adição à resposta de Brent ao usar um índice não coberto para evitar uma classificação, existe um possível problema com números de páginas posteriores que podem ser vistos na execução do abaixo

SELECT * 
FROM dbo.Users 
ORDER BY DisplayName 
OFFSET 100000 ROWS 
FETCH NEXT 100 ROWS ONLY;

O plano de execução mostra que a pesquisa foi executada 100.100 vezes, embora todas, exceto 100 linhas, sejam filtradas pelo operador TOP.

insira a descrição da imagem aqui

Isso pode ser mitigado usando o padrão abaixo

WITH T
     AS (SELECT Id,
                DisplayName
         FROM   dbo.Users
         ORDER  BY DisplayName
        OFFSET 100000 ROWS 
        FETCH NEXT 100 ROWS ONLY
        )
SELECT U.*
FROM   dbo.Users U
       JOIN T
         ON U.Id = T.Id
ORDER  BY T.DisplayName 

Isso filtra tudo, exceto as 100 linhas finais, antes de fazer as pesquisas, que podem ter um impacto significativo na velocidade para grandes valores de deslocamento.

insira a descrição da imagem aqui

Martin Smith
fonte
3

Realmente depende de como você implementa a paginação em sua consulta, a natureza dos dados e a maneira como seu sistema está configurado. É bastante seguro dizer que o SQL Server tentará retornar seus dados usando o que parece ser o menor esforço possível. Se você não tiver uma ordem de classificação explícita, filtragem, agrupamento ou janela, o SQL Server poderá otimizar o plano de consulta, de forma a retornar apenas as páginas do disco que continham os dados exigidos pela sua consulta - ou, melhor ainda, diretamente do buffer pool. Assim que você começar a alterar a consulta para incluir classificação, agrupamento, janela e filtragem, ela começará a ficar complicada.

Há um artigo muito bom sobre o desempenho do SQL aqui que detalha os vários métodos de paginação e como eles afetam o plano de consulta. Eu recomendo lê-lo e, em seguida, experimentar alguns dos vários métodos que eles apontam e ver qual plano de consulta é escolhido em seu próprio sistema.

Mr.Brownstone
fonte