使用SetFirstResult(start)
和SetMaxResults(count)
方法实现分页时,我注意到生成的查询只执行select top count * from some_table
并且不考虑start
参数或至少不在数据库级别。看来如果我指示NHibernate执行以下查询:
var users = session.CreateCriteria<User>()
.SetFirstResult(100)
.SetMaxResults(5)
.List<User>();
105条记录将在数据库服务器和应用程序之间传输,这将记录前100条记录。对于包含许多行的表,这可能是个问题。
我已经通过SQLite数据库验证了NHibernate利用OFFSET
和LIMIT
关键字来过滤数据库级别的结果。我知道SQL Server 2000中没有等效的OFFSET
关键字和Oracle的ROWNUM
,但有没有解决方法? SQL Server 2005/2008怎么样?
答案 0 :(得分:16)
T-SQL是Microsoft SQL Server使用的SQL语言的变体,它没有limit
子句。它有一个select top {...}
修饰符,你可以看到NHibernate利用了SQL Server 2000。
使用SQL Server 2005,Microsoft引入了Row_Number() over (order by {...})
函数,可用作limit
子句的替代,您可以看到NHibernate利用SQL Server 2005/2008的优势。 / p>
SQLite的查询可能看起来像
select c.[ID], c.[Name]
from [Codes] c
where c.[Key] = 'abcdef'
order by c.[Order]
limit 20 offset 40
虽然SQL Server 2005的类似查询可能看起来像
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]
或使用Common Table Expressions,它可能看起来像
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]
有一种方法可以在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]
答案 1 :(得分:4)
Nhibernate非常聪明,可以优化查询。如果您选择前10行,它将使用TOP
语句。如果您不选择第一行,则会使用RowNum
。
在sql 2000中没有RowNum
函数,这就是为什么通常的查询不可能选择所需的行数。对于sql 2000,因为我知道这样的优化视图被使用。
在sql 2005/2008中,查询将只选择所需的行。