Paging di NHibernate con SQL Server
-
22-07-2019 - |
Domanda
Quando si utilizzano i metodi SetFirstResult (start)
e SetMaxResults (count)
per implementare il paging ho notato che la query generata fa solo un seleziona il conteggio superiore * da some_table
e non tiene conto del parametro start
o almeno non a livello di database. Sembra che se do istruzioni a NHibernate di eseguire la seguente query:
var users = session.CreateCriteria<User>()
.SetFirstResult(100)
.SetMaxResults(5)
.List<User>();
105 record passeranno tra il server di database e l'applicazione che avrà cura di eliminare i primi 100 record. Con tabelle contenenti molte righe questo potrebbe essere un problema.
Ho verificato che con un database SQLite NHibernate sfrutta il OFFSET
e LIMIT
per filtrare i risultati a livello di database. Sono consapevole che non esiste un equivalente della parola chiave OFFSET
e del ROWNUM
di Oracle in SQL Server 2000 ma esiste qualche soluzione? Che ne dici di SQL Server 2005/2008?
Soluzione
T-SQL, la variante del linguaggio SQL utilizzato da Microsoft SQL Server, non ha una clausola limit
. Ha un modificatore seleziona top {...}
di cui vedi NHibernate approfittare di SQL Server 2000.
Con SQL Server 2005, Microsoft ha introdotto la funzione Row_Number () over (ordina per {...})
che può essere utilizzata in sostituzione della clausola limit
e puoi vedere NHibernate approfittarne con SQL Server 2005/2008.
Una query per SQLite potrebbe apparire
select c.[ID], c.[Name]
from [Codes] c
where c.[Key] = 'abcdef'
order by c.[Order]
limit 20 offset 40
mentre una query simile per SQL Server 2005 potrebbe apparire
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]
o, usando Common Table Expressions, potrebbe apparire come
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]
Esiste un modo per farlo anche in SQL Server 2000
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]
Altri suggerimenti
Nhibernate è abbastanza intelligente da ottimizzare la query. Se selezioni le prime 10 righe utilizzerà l'istruzione TOP
. Se selezioni non le prime righe, utilizzerà RowNum
.
In sql 2000 non esiste la funzione RowNum
, ecco perché è impossibile con la normale query selezionare il numero richiesto di righe. Per sql 2000, per quanto ne so, sono state utilizzate viste di ottimizzazione.
In sql 2005/2008 la query selezionerà solo le righe richieste.