Obter “circundante” linhas na consulta NHibernate
-
22-07-2019 - |
Pergunta
Eu estou procurando uma maneira de recuperar as linhas "cercam" em uma consulta NHibernate dada uma chave primária e uma ordem de classificação?
por exemplo. Eu tenho uma tabela com entradas de registo e eu quero mostrar a entrada com chave primária 4242 e os anteriores 5 entradas, bem como os seguintes 5 entradas ordenados por data (não há relação direta entre a data ea chave primária). Tal consulta deve retornar 11 linhas no total (desde que não estamos perto de cada extremidade).
A tabela a entrada de log pode ser enorme e recuperar tudo para descobrir isso não é possível.
Existe um conceito como número da linha que pode ser usado a partir de dentro NHibernate? O banco de dados subjacente é ou vai ser SQlite ou Microsoft SQL Server.
Editado Amostra adicionado
Imagine que dados como o seguinte:
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
Ao solicitar a entrada com chave primária 4242 devemos obter as linhas 1237, 1238 e 4239 a 4247. A ordem é por Tempo, Id.
É possível recuperar as entradas em uma única consulta (que, obviamente, pode incluir subconsultas)? O tempo é uma coluna não-exclusivo para várias entradas têm o mesmo valor e, neste exemplo, não é possível alterar a resolução de uma forma que torna! Única
Solução
"não há relação direta entre a data e primário-chave" significa, que as chaves primárias não estão em ordem sequencial?
Então eu iria fazê-lo como este:
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);
Há o risco de ter vários itens com o mesmo tempo.
Editar
Isso deve funcionar agora.
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);
Outras dicas
Esta deve ser relativamente fácil com a API Critérios de NHibernate:
List<LogEntry> logEntries = session.CreateCriteria(typeof(LogEntry))
.Add(Expression.InG<int>(Projections.Property("Id"), listOfIds))
.AddOrder(Order.Desc("EntryDate"))
.List<LogEntry>();
Aqui o seu listOfIds
é apenas uma lista com rigidez de tipos inteiros que representam os ids das entradas que deseja recuperar (inteiros 4242-5 através de 4242 + 5).
É claro que você também pode adicionar Expressions
que lhe permitem recuperar IDs maior do que 4242-5 e menor do que 4242 + 5.
A solução de Stefan definitivamente funciona, mas a melhor maneira existe usando um único Subqueries selecionar e aninhados:
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>();
Esta é NHibernate sintaxe 2.0, mas o mesmo vale para as versões anteriores onde em vez de restrições que você usar a expressão.
Eu testei isso em um aplicativo de teste e funciona como anunciado