2009-10-30 22 views
13

Al utilizar SetFirstResult(start) y SetMaxResults(count) métodos para implementar la paginación Me he dado cuenta de que la consulta generada sólo lo hace un select top count * from some_table y no se necesita el parámetro start en cuenta o al menos no en el nivel de base de datos. Parece que si instruyo NHibernate para ejecutar la siguiente consulta:paginación NHibernate con SQL Server

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

105 registros transitará entre el servidor de base de datos y la aplicación que se encargará de despojar 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 se aprovecha de las OFFSET y LIMIT palabras clave para filtrar los resultados a nivel de base de datos. Soy consciente de que no hay equivalente de la palabra clave OFFSET y Oracle de ROWNUM en SQL Server 2000 pero ¿hay alguna solución? ¿Qué hay de SQL Server 2005/2008?

Respuesta

16

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

Con SQL Server 2005, Microsoft introdujo la función Row_Number() over (order by {...}) que puede ser utilizado como un sustituto de la cláusula limit, y se puede ver NHibernate aprovechando de eso con SQL Server 2005/2008.

Una consulta para SQLite podría ser similar

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 ser similar

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, puede ser que parezca

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] 

Hay una manera de hacerlo en SQL Server 2000 también

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

Gran respuesta @Justicia. Gracias por tu tiempo. –

4

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

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

en SQL 2005/2008 consulta seleccionará filas sólo se requiere.


+3

He verificado que con sql 2005/2008 NHibernate utiliza la función 'row_number()'. Parece que con sql 2000 tengo que escribir vistas o procedimientos almacenados para lograr el mismo efecto. –

Cuestiones relacionadas