Question

Lors de l'utilisation de SetFirstResult (start) et de méthodes SetMaxResults (count) pour implémenter la pagination, j'ai remarqué que la requête générée ne fait qu'un select top count * de some_table et le paramètre start n'est pas pris en compte, ou du moins pas au niveau de la base de données. Il semble que si je demande à NHibernate d’exécuter la requête suivante:

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

105 enregistrements transitent entre le serveur de base de données et l’application, qui se chargera de supprimer les 100 premiers enregistrements. Avec les tables contenant plusieurs lignes, cela pourrait poser problème.

J'ai vérifié qu'avec une base de données SQLite , NHibernate tire parti du OFFSET et LIMIT pour filtrer les résultats au niveau de la base de données. Je suis conscient du fait qu'il n'existe pas d'équivalent entre le mot clé OFFSET et le ROWNUM d'Oracle dans SQL Server 2000, mais existe-t-il une solution de contournement? Qu'en est-il de SQL Server 2005/2008?

Était-ce utile?

La solution

T-SQL, la variante du langage SQL utilisée par Microsoft SQL Server, ne comporte pas de clause limit . Il possède un modificateur select top {...} dont NHibernate tire parti avec SQL Server 2000.

Avec SQL Server 2005, Microsoft a introduit la fonction Row_Number () over (order by {...}) , qui peut être utilisée en remplacement de la clause limit . et vous pouvez voir NHibernate en tirer parti avec SQL Server 2005/2008.

Une requête pour SQLite pourrait ressembler à

select c.[ID], c.[Name]
from [Codes] c
where c.[Key] = 'abcdef'
order by c.[Order]
limit 20 offset 40

alors qu'une requête similaire pour SQL Server 2005 pourrait ressembler à

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, à l'aide d'expressions de table communes, cela pourrait ressembler à

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]

Il existe également un moyen de le faire dans 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]

Autres conseils

Nhibernate est suffisamment intelligent pour optimiser les requêtes. Si vous sélectionnez les 10 premières lignes, il utilisera l'instruction TOP . Si vous ne sélectionnez pas les premières lignes, il utilisera RowNum .

Dans SQL 2000, il n’existe pas de fonction RowNum . C’est pourquoi il est impossible avec une requête classique de sélectionner le nombre de lignes requis. Pour SQL 2000, comme je le sais pour une telle optimisation, des vues ont été utilisées.

Dans SQL 2005/2008, la requête sélectionne uniquement les lignes requises.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top