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?

È stato utile?

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.


Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top