paginação NHibernate com o SQL Server
-
22-07-2019 - |
Pergunta
Ao usar métodos SetFirstResult(start)
e SetMaxResults(count)
implementar a paginação tenho notado que a consulta gerada apenas faz um select top count * from some_table
e ele não leva o parâmetro start
em conta ou, pelo menos não no nível de banco de dados. Parece que se eu instruir NHibernate para executar a consulta a seguir:
var users = session.CreateCriteria<User>()
.SetFirstResult(100)
.SetMaxResults(5)
.List<User>();
105 registros irão transitar entre o servidor de banco de dados ea aplicação que terá o cuidado de retirar os primeiros 100 registros. Com tabelas contendo várias linhas isso poderia ser um problema.
eu tenho verificado que, com uma SQLite banco de dados NHibernate aproveita as palavras-chave OFFSET
e LIMIT
para filtrar os resultados no nível do banco de dados. Estou ciente de que não há nenhum equivalente da palavra-chave OFFSET
e ROWNUM
da Oracle no SQL Server 2000 mas existe alguma solução? Como sobre o SQL Server 2005/2008?
Solução
T-SQL, a variante da linguagem SQL que usa o Microsoft SQL Server, não tem uma cláusula limit
. Ele tem um modificador select top {...}
que você vê NHibernate aproveitando com o SQL Server 2000.
Com o SQL Server 2005, a Microsoft introduziu a função Row_Number() over (order by {...})
que pode ser usado como um substituto para a cláusula limit
, e você pode ver NHibernate aproveitando que com o SQL Server 2005/2008.
A consulta para SQLite pode parecer
select c.[ID], c.[Name]
from [Codes] c
where c.[Key] = 'abcdef'
order by c.[Order]
limit 20 offset 40
enquanto uma consulta semelhante para o SQL Server 2005 pode parecer
select c.[ID], c.[Name]
from (
select c.[ID], c.[Name], c.[Order]
, [!RowNum] = Row_Number() over (order by c.[Order])
from [Codes] c
where c.[Key] = 'abcdef'
) c
where c.[!RowNum] > 40 and c.[!RowNum] <= 60
order by c.[Order]
ou, usando expressões de tabela comum, que pode parecer
with
[Source] as (
select c.[ID], c.[Name], c.[Order]
, [!RowNum] = Row_Number() over (order by c.[Order])
from [Codes] c
where c.[Key] = 'abcdef'
)
select c.[ID], c.[Name]
from [Source] c
where c.[!RowNum] > 40 and c.[!RowNum] <= 60
order by c.[Order]
Há uma maneira de fazê-lo em SQL Server 2000, bem
select c.[ID], c.[Name]
from (
select top 20 c.[ID], c.[Name], c.[Order]
from (
select top 60 c.[ID], c.[Name], c.[Order]
from [Codes] c
where c.[Key] = 'abcdef'
order by c.[Order]
) c
order by c.[Order] desc
) c
order by c.[Order]
Outras dicas
Nhibernate é suficiente inteligente para consulta otimizar. Se seleccionar primeiro 10 linhas que vai usar declaração TOP
. Se você selecionar Não primeiras filas, então ele irá usar RowNum
.
No SQL 2000 não há nenhuma função RowNum
, é por isso que é impossível com consulta habitual para selecionar o número necessário de linhas. Para o SQL 2000 como eu sei que para tal vista de otimização foram utilizados.
Em sql 2005/2008 consulta irá selecionar apenas linhas necessárias.