Como a paginação funciona com ROW_NUMBER no SQL Server?

13

Eu tenho uma Employeetabela que tem um milhão de registros. Eu tenho o seguinte SQL para paginar dados em um aplicativo da web. Está funcionando bem. No entanto, o que eu vejo como um problema é - a tabela derivada tblEmployeeseleciona todos os registros da Employeetabela (para criar os MyRowNumbervalores).

Eu acho que isso causa a seleção de todos os registros na Employeetabela.

Isso realmente funciona? Ou o SQL Server é otimizado para selecionar também apenas os 5 registros da Employeetabela original ?

DECLARE @Index INT;
DECLARE @PageSize INT;

SET @Index = 3;
SET @PageSize = 5;

SELECT *  FROM
  (SELECT  ROW_NUMBER() OVER (ORDER BY EmpID asc) as MyRowNumber,*
  FROM Employee) tblEmployee
WHERE MyRowNumber BETWEEN ( ((@Index - 1) * @PageSize )+ 1) AND @Index*@PageSize 
LCJ
fonte
3
Consulte sqlservercentral.com/articles/T-SQL/66030 e, mais importante, a discussão que se segue .
Aaron Bertrand

Respostas:

17

Uma alternativa ao teste pode ser:

;WITH x AS (SELECT EmpID, k = ROW_NUMBER() OVER (ORDER BY EmpID) FROM dbo.Emp)
SELECT e.columns
FROM x INNER JOIN dbo.Emp AS e
ON x.EmpID = e.EmpID
WHERE x.k BETWEEN (((@Index - 1) * @PageSize) + 1) AND @Index * @PageSize
ORDER BY ...;

Sim, você bate na tabela duas vezes, mas no CTE, onde você varre a tabela inteira, está apenas pegando a chave, não TODOS os dados. Mas você realmente deve olhar para este artigo:

http://www.sqlservercentral.com/articles/T-SQL/66030/

E a discussão de acompanhamento:

http://www.sqlservercentral.com/Forums/Topic672980-329-1.aspx

No SQL Server 2012, é claro, você pode usar a nova OFFSET/ FETCH NEXTsintaxe:

;WITH x AS 
(
  SELECT EmpID FROM dbo.Emp
    ORDER BY EmpID
    OFFSET  @PageSize * (@Index - 1) ROWS
    FETCH NEXT @PageSize ROWS ONLY
)
SELECT e.columns
FROM x INNER JOIN dbo.Emp AS e
ON x.EmpID = e.EmpID
ORDER BY ...; 
Aaron Bertrand
fonte
Note-se que embora OFFSET / FETCH PRÓXIMO não oferecem quaisquer benefícios de desempenho sobre o método CTE
Akash
2
@ Akash você já testou isso completamente? Observei algumas diferenças de plano, mas não mencionei nada especificamente sobre o desempenho porque não fiz nenhum teste extenso. Mesmo que o desempenho seja o mesmo, a sintaxe é um pouco menos complicada. Eu publiquei um
Aaron Bertrand
1
Ah, você está certo, há uma diferença de desempenho. Eu tinha lido o seguinte: blogs.technet.com/b/dataplatforminsider/archive/2011/11/01/… onde ele menciona nenhuma diferença, mas apenas vi channel9.msdn.com/posts/SQL11UPD03-REC-02 onde ele mostra muita diferença .. (embora no áudio underemphasis a diferença de desempenho)
Akash
2

Embora você não conheça o mecanismo por trás dele, você mesmo pode testar isso comparando o desempenho da sua consulta com: selecione * em Funcionário.

As versões mais recentes do SQL Server fazem um ótimo trabalho de otimização, mas isso pode depender de vários fatores.

O desempenho da sua função ROW_NUMBER será determinado pela cláusula Order By. No seu exemplo, a maioria pensaria que o EmpID é a chave primária.

Existem algumas cláusulas where que são tão complexas e / ou mal codificadas ou indexadas, é melhor retornar apenas o conjunto de dados inteiro (é raro e pode ser corrigido). O uso de BETWEEN tem problemas.

Antes de assumir que seria melhor retornar todas as linhas para o seu aplicativo e deixá-lo descobrir, você deve otimizar sua consulta. Verifique as estimativas. Pergunte ao Query Analyzer. Teste algumas alternativas.

JeffO
fonte
2

Eu sei que a pergunta é sobre row_number (), mas quero adicionar um novo recurso do sql server 2012. No sql server 2012, o novo recurso OFFSET Fetch é apresentado a seguir e é muito rápido que o row_number (). Eu usei e me dá um bom resultado espero que vocês também preencham a mesma experiência.

Encontrei um exemplo em http://blogfornet.com/2013/06/sql-server-2012-offset-use/

o que é útil. Espero que também o ajude a implementar novos recursos ....

Sam Raj
fonte
-2

Eu não acho que ele avalia retornar todas as linhas na tabela original. O servidor SQL otimiza. Caso contrário, levará muito tempo para selecionar um milhão de entradas. Atualmente, estou usando isso e é muito mais rápido do que selecionar todas as linhas. Portanto, com certeza não obtém todas as linhas. No entanto, é mais lento do que apenas buscar as cinco primeiras linhas, provavelmente devido ao tempo gasto no pedido

user3688672
fonte
-2
DECLARE @PageIndex int;
DECLARE @PageSize int;
SET @PageIndex = 4;
SET @PageSize = 5;
;With ranked AS   --- Or you can make it a view
(
   SELECT ROW_NUMBER() OVER(ORDER BY IdentityId) AS RowNum,  *
   FROM logeventnew
)
SELECT *   --Your fields here
FROM Ranked
WHERE RowNum BETWEEN ((@PageIndex - 1) * @PageSize + 1)
    AND (@PageIndex * @PageSize)
ORDER BY IdentityId
Agnel Amodia
fonte
4
Você poderia expandir sua resposta? A questão era sobre como a paginação funciona internamente no SQL Server - ou seja, o que o mecanismo de banco de dados faz para atender à solicitação. Infelizmente, a partir de agora, sua resposta não aborda a preocupação real.
Mr.Brownstone