取得“周囲” NHibernateクエリの行
-
22-07-2019 - |
質問
「周囲」を取得する方法を探しています。主キーとソート順が指定されたNHibernateクエリの行?
E.g。ログエントリを含むテーブルがあり、主キー4242と前の5つのエントリ、および日付順に並べられた次の5つのエントリを持つエントリを表示します(日付と主キーの間に直接の関係はありません)。このようなクエリは、合計で11行を返す必要があります(どちらかの端に近づいていない限り)。
ログエントリテーブルは非常に大きくなる可能性があるため、すべてを取得して把握することはできません。
NHibernate内から使用できる行番号などの概念はありますか?基礎となるデータベースは、SQliteまたはMicrosoft SQL Serverのいずれかになります。
編集済みサンプルの追加
次のようなデータを想像してください:
Id Time
4237 10:00
4238 10:00
1236 10:01
1237 10:01
1238 10:02
4239 10:03
4240 10:04
4241 10:04
4242 10:04 <-- requested "center" row
4243 10:04
4244 10:05
4245 10:06
4246 10:07
4247 10:08
プライマリキー4242を使用してエントリを要求する場合、行1237、1238、および4239から4247を取得する必要があります。順序は、時間、Idです。
単一のクエリでエントリを取得することは可能ですか(明らかにサブクエリを含めることができます)?時間は一意ではない列であるため、いくつかのエントリは同じ値を持ち、この例では、一意になるように解像度を変更することはできません!
解決
&quot;日付と主キーの間に直接の関係はありません&quot;つまり、主キーが順番に並んでいませんか?
その後、次のようにします:
Item middleItem = Session.Get(id);
IList<Item> previousFiveItems = Session.CreateCriteria((typeof(Item))
.Add(Expression.Le("Time", middleItem.Time))
.AddOrder(Order.Desc("Time"))
.SetMaxResults(5);
IList<Item> nextFiveItems = Session.CreateCriteria((typeof(Item))
.Add(Expression.Gt("Time", middleItem.Time))
.AddOrder(Order.Asc("Time"))
.SetMaxResults(5);
複数のアイテムが同時に存在するリスクがあります。
編集
これで動作するはずです。
Item middleItem = Session.Get(id);
IList<Item> previousFiveItems = Session.CreateCriteria((typeof(Item))
.Add(Expression.Le("Time", middleItem.Time)) // less or equal
.Add(Expression.Not(Expression.IdEq(middleItem.id))) // but not the middle
.AddOrder(Order.Desc("Time"))
.SetMaxResults(5);
IList<Item> nextFiveItems = Session.CreateCriteria((typeof(Item))
.Add(Expression.Gt("Time", middleItem.Time)) // greater
.AddOrder(Order.Asc("Time"))
.SetMaxResults(5);
他のヒント
NHibernateのCriteria APIを使用すると、これは比較的簡単になります。
List<LogEntry> logEntries = session.CreateCriteria(typeof(LogEntry))
.Add(Expression.InG<int>(Projections.Property("Id"), listOfIds))
.AddOrder(Order.Desc("EntryDate"))
.List<LogEntry>();
ここで、 listOfIds
は、取得するエントリのID(整数4242-5〜4242 + 5)を表す、厳密に型指定された整数のリストです。
もちろん、 Expressions
を追加して、4242-5より大きく4242 + 5より小さいIDを取得できます。
Stefanのソリューションは間違いなく機能しますが、単一の選択およびネストされたサブクエリを使用するより良い方法が存在します:
ICriteria crit = NHibernateSession.CreateCriteria(typeof(Item));
DetachedCriteria dcMiddleTime =
DetachedCriteria.For(typeof(Item)).SetProjection(Property.ForName("Time"))
.Add(Restrictions.Eq("Id", id));
DetachedCriteria dcAfterTime =
DetachedCriteria.For(typeof(Item)).SetMaxResults(5).SetProjection(Property.ForName("Id"))
.Add(Subqueries.PropertyGt("Time", dcMiddleTime));
DetachedCriteria dcBeforeTime =
DetachedCriteria.For(typeof(Item)).SetMaxResults(5).SetProjection(Property.ForName("Id"))
.Add(Subqueries.PropertyLt("Time", dcMiddleTime));
crit.AddOrder(Order.Asc("Time"));
crit.Add(Restrictions.Eq("Id", id) || Subqueries.PropertyIn("Id", dcAfterTime) ||
Subqueries.PropertyIn("Id", dcBeforeTime));
return crit.List<Item>();
これはNHibernate 2.0構文ですが、制限を使用する代わりにExpressionを使用する以前のバージョンでも同じです。
テストアプリケーションでこれをテストしましたが、広告どおりに機能しています