2009-10-30 16 views
13

Quando si utilizza SetFirstResult(start) e SetMaxResults(count) metodi per implementare il paging ho notato che la query generata fa solo un select top count * from some_table e non ci vuole il parametro start in considerazione o almeno non a livello di database. Sembra che se Incarico NHibernate per eseguire la seguente query:NHibernate paginazione con SQL Server

var users = session.CreateCriteria<User>() 
        .SetFirstResult(100) 
        .SetMaxResults(5) 
        .List<User>(); 

105 record transiterà tra il server di database e l'applicazione, che si prenderà cura per mettere a nudo i primi 100 record. Con tabelle contenenti molte righe questo potrebbe essere un problema.

Ho verificato che con un database SQLite NHibernate sfrutta le parole chiave OFFSET e LIMIT per filtrare i risultati a livello di database. Sono a conoscenza del fatto che non esiste un equivalente della parola chiave OFFSET e Oracle ROWNUM in SQL Server 2000 ma esiste una soluzione alternativa? Che ne dici di SQL Server 2005/2008?

risposta

16

T-SQL, la variante del linguaggio SQL che utilizza Microsoft SQL Server, non ha una clausola limit. Ha un select top {...} modificatore che vedete NHibernate sfruttando con SQL Server 2000.

Con SQL Server 2005, Microsoft ha introdotto la funzione Row_Number() over (order by {...}) che può essere utilizzato in sostituzione alla clausola limit, e si può vedere NHibernate approfittando di quello con SQL Server 2005/2008.

Una query per SQLite potrebbe apparire come

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 essere simile

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 espressioni di tabella comuni, potrebbe assomigliare

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] 

C'è 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] 
+0

Ottima risposta alla giustizia. Grazie per il tuo tempo. –

4

Nhibate è abbastanza intelligente per ottimizzare la query. Se selezioni le prime 10 righe, verrà utilizzata la dichiarazione TOP. Se si seleziona non prime righe, verrà utilizzato RowNum.

In sql 2000 non esiste la funzione RowNum, è per questo che è impossibile con la normale query selezionare il numero di righe richiesto. Per sql 2000, come so, sono state utilizzate visualizzazioni di ottimizzazione.

Nella query 2005/2008 selezionerà solo le righe richieste.


+3

Ho verificato che con sql 2005/2008 NHibernate utilizza la funzione 'row_number()'. Sembra che con sql 2000 devo scrivere viste o stored procedure per ottenere lo stesso effetto. –