Pregunta

Cuando se usan los métodos SetFirstResult (inicio) y SetMaxResults (count) para implementar la paginación, he notado que la consulta generada solo hace un select top count * de some_table y no tiene en cuenta el parámetro start o al menos no a nivel de la base de datos. Parece que si le digo a NHibernate que ejecute la siguiente consulta:

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

105 registros transitarán entre el servidor de la base de datos y la aplicación que se encargará de eliminar los primeros 100 registros. Con tablas que contienen muchas filas esto podría ser un problema.

He verificado que con una base de datos SQLite , NHibernate aprovecha el código OFFSET y LIMIT palabras clave para filtrar resultados a nivel de base de datos. Soy consciente de que no hay un equivalente de la palabra clave OFFSET y ROWNUM de Oracle en SQL Server 2000, pero ¿hay alguna solución? ¿Qué hay de SQL Server 2005/2008?

¿Fue útil?

Solución

T-SQL, la variante del lenguaje SQL que utiliza Microsoft SQL Server, no tiene una cláusula limit . Tiene un modificador select top {...} que puede ver que NHibernate aprovecha con SQL Server 2000.

Con SQL Server 2005, Microsoft introdujo la función Row_Number () over (ordenar por {...}) que se puede usar como reemplazo de la cláusula limit , y puede ver que NHibernate se aprovecha de eso con SQL Server 2005/2008.

Una consulta para SQLite podría verse

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

mientras que una consulta similar para SQL Server 2005 podría verse

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 Expresiones de tabla comunes, podría 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]

También hay una manera de hacerlo en 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]

Otros consejos

Nhibernate es lo suficientemente inteligente como para optimizar la consulta. Si selecciona las primeras 10 filas, usará la instrucción TOP . Si selecciona no las primeras filas, usará RowNum .

En sql 2000 no existe la función RowNum , es por eso que con la consulta habitual es imposible seleccionar el número requerido de filas. Para sql 2000, como sé, se utilizaron vistas de optimización.

En sql 2005/2008 la consulta seleccionará solo las filas requeridas.


Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top