SQL Serverを使用したNHibernateページング
-
22-07-2019 - |
質問
SetFirstResult(start)
および SetMaxResults(count)
メソッドを使用してページングを実装すると、生成されたクエリは select top count *のみを実行することに気付きました。 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はどうですか?
解決
T-SQLには、 limit
句がありません。 NHibernateがSQL Server 2000を利用していることがわかる select top {...}
修飾子があります。
SQL Server 2005で、Microsoftは limit
句の代わりとして使用できる Row_Number()over(order by {...})
関数を導入しました、そしてNHibernateがSQL Server 2005/2008でそれを利用しているのを見ることができます。
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]
または共通テーブル式を使用すると、次のようになります
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]
他のヒント
Nhibernateはクエリを最適化するのに十分スマートです。最初の10行を選択すると、 TOP
ステートメントが使用されます。最初の行以外を選択すると、 RowNum
が使用されます。
SQL 2000には RowNum
関数はありません。そのため、通常のクエリでは必要な行数を選択することはできません。私が知っているようなSQL 2000では、そのような最適化ビューが使用されました。
SQL 2005/2008のクエリでは、必要な行のみが選択されます。