I am using NHibernate 3.3.1.4000.
Here's what I would like to achieve:
SELECT sum(tr_time)
FROM (SELECT seg.translation_time as tr_time
FROM "TRANSLATION" seg
WHERE seg.translation_time > 0 and seg.fk_id_language = 34
ORDER BY seg.translation_time ASC
LIMIT 1000) as subquery
The easiest way, using LINQ to NHibernate, fails with a NotSupportedException:
session.Query<Translation>()
.Where(s => s.Language == language && s.TranslationTime > 0)
.OrderBy(s => s.TranslationTime)
.Take(1000)
.Select(s => s.TranslationTime)
.Sum(s => s);
I was able to produce this SQL:
SELECT sum(this_.translation_time) as y0_
FROM "TRANSLATION" this_
WHERE this_.id in (SELECT this_0_.id as y0_
FROM "TRANSLATION" this_0_
WHERE this_0_.fk_id_language = 34
and this_0_.translation_time > 0
ORDER BY this_0_.translation_time asc
LIMIT 1000)
with the following query:
session.QueryOver<Translation>(() => translationAlias)
.WithSubquery.WhereProperty(s => s.Id)
.In(QueryOver.Of<Translation>(() => translationSubAlias)
.Where(s => s.Language == language)
.Where(s => s.TranslationTime > 0)
.Select(s => s.Id)
.OrderBy(s => s.TranslationTime).Asc
.Take(1000))
.Select(Projections.ProjectionList()
.Add(Projections.Sum(Projections.Property(() => translationAlias.TranslationTime))))
.FutureValue<Int32>();
However, that's obviously not ideal as the WHERE ... IN clause is not necessary.
I tried things like
session.QueryOver<Translation>(() => translationAlias)
.Select(Projections.ProjectionList()
.Add(Projections.Sum(
Projections.Alias(
Projections.SubQuery(
QueryOver.Of<Translation>(() => translationSubAlias)
.Where(() => translationSubAlias.Language == language)
.Where(() => translationSubAlias2.TranslationTime > 0)
.Select(Projections.Property("TranslationTime"))), "tr_time"))))
.FutureValue<Int32>()
but it generates this SQL:
SELECT sum((SELECT this_0_.translation_time as y0_
FROM "TRANSLATION" this_0_
WHERE this_0_.fk_id_language = 34 and this_0_.translation_time > 0)) as y0_
FROM "TRANSLATION" this_
which the database server rejects with the following error message: more than one row returned by a subquery used as an expression
I don't really care which method I would eventually use (LINQ, QueryOver, Criteria API) as long as it allows future queries.
Any chance it is not returning any values? If that is the case, you need to make TranslationTime nullable:
session.Query<Translation>()
.Where(s => s.Language == language && s.TranslationTime > 0)
.OrderBy(s => s.TranslationTime)
.Take(1000)
.Select(s => s.TranslationTime)
.Sum(s => (int?) s);
Notice the "int?" cast, assuming TranslationTime is of type int.
Related
I have some QueryOver with several joins and the result I get is OK in terms of the returned objects. This is the code>
var l = session.QueryOver<Discount>(() => discount)
.JoinQueryOver<ConPrdGrp>(r => r.ConPrdGrps)
.JoinQueryOver<PrdGroupTree>(k => k.PrdGroupTree)
.JoinQueryOver<Product>(g => g.Products)
.Where(p => p.ProductID == Product.ProductID)
.And(() => discount.FomDato <= DateTime.Now && discount.TomDato >= DateTime.Now).List();
But if I look at the SQL statement, I can see that the generated query selects ALL columns from all joined tables although the result only returns a list of Discount objects. I can get some properties of Discount using projections and the query will be much smaller. But how I can instruct NHibernate to get just columns from the Discount table and not from all joined tables?
As far as I know there is NOT a simple way like: .Select(Projections.Properties<Discount>()). Check this a bit outdated question (NHIbernate: Shortcut for projecting all properties?)
What you can do is explicitly name the columns you would like to see:
... // the QueryOver you have
.SelectList (l => l
.Select(() => discount.ID).WithAlias(() => discount.ID)
.Select(() => discount.Code).WithAlias(() => discount.Code)
...
)
.TransformUsing(Transformers.AliasToBean<Discount>())
...
The second approach could be to create the Subquery, and then just QueryOver<Discount>() 16.8. Subqueries
QueryOver<Discount> subQuery =
QueryOver.Of<Discount>(() => innerDiscount)
.JoinQueryOver<ConPrdGrp>(r => r.ConPrdGrps)
.JoinQueryOver<PrdGroupTree>(k => k.PrdGroupTree)
.JoinQueryOver<Product>(g => g.Products)
.Where(p => p.ProductID == Product.ProductID)
.And(() => innerDiscount.FomDato <= DateTime.Now
&& innerDiscount.TomDato >= DateTime.Now).List()
.Select( Projections.Property(() => innerDiscount.ID))
;
var query = session.QueryOver<Discount>(() => discount)
.Where(Subqueries.PropertyIn("ID", subQuery.DetachedCriteria))
;
I need help converting this sql query into QueryOver Nhibernate criteria.
select distinct * from event e where e.name like '%req%'
or e.Id in (select r.eventId from requirement r where r.name like '%req%')
or e.Id in (select r.eventId from requirement r where r.id
in (select s.requirementId from solution s where s.name like '%sol%'))
var queryOver = session.QueryOver<Event>()
.Where(x => x.Name.IsInsensitiveLike("%"+searchTerms[1]+"%"))
.OrderBy(x => x.CreatedOn).Asc;
So far I have the main query but couldn't find enough reference material on how to add the subqueries. Haven't been successful using joinQueryOver.
Event has one-to-many rel with requirement and requirement has one-to-many rel with solution.
Requirement reqAlias = null;
Solution solAlias = null;
var subQuery = QueryOver.Of<Event>()
.JoinAlias(x => x.Requirements, () => reqAlias)
.Where(x => x.Name.IsInsensitiveLike(searchTerms[2]))
.JoinAlias(() => reqAlias.Solutions, () => solAlias)
.Where(x => x.Name.IsInsensitiveLike(searchTerms[3]))
.Select(Projections.Group<Event>(x => x.Id));
var events = session.QueryOver<Event>()
.Where(x => x.Name.IsInsensitiveLike(searchTerms[1]))
.WithSubquery.WhereProperty(x => x.Id).In(subQuery)
.List().ToList();
still not working.
When you use IsInsensitiveLike NHibernate appends the % after parsing, and uses lower to do a lower case comparison. In your code, you are appending the % yourself, which results in,
select distinct * from event e where e.name like %lower('%req%')%
which in turn, doesn't work.
Also, you have 3 subqueries, no a big one, so you need to restructure your code to account for that:
select r.eventId from requirement r where r.name like '%req%'
to
var firstQuery = QueryOver.Of<Requirement>()
.Where(r => r.Name.IsInsensitiveLike(searchTerms[2]))
.Select(r => r.EventId);
then,
select s.requirementId from solution s where s.name like '%sol%'
to
var solutionQuery = QueryOver.Of<Solution>()
.Where(s => s.Name.IsInsensitiveLike(searchTerms[3]));
then,
select r.eventId from requirement r where r.id
in (select s.requirementId from solution s where s.name like '%sol%')
to
var requirementQuery = QueryOver.Of<Requirement>()
.WithSubquery
.WhereProperty(r => r.Id).In(solutionQuery)
.Select(r => r.EventId);
Then you need to construct the main query using Restrictions.Or to include the 3 queries.
Using the Criteria API, I can generate a query that creates a JOIN with an extra condition on the JOIN
var criteria = Session.CreateCriteria<Product>()
.SetReadOnly(true)
.SetMaxResults(1)
.CreateAlias("ProductCategory", "U", JoinType.LeftOuterJoin, Expression.Eq("U.SubType", "Premium"))
.AddOrder(Order.Desc("U.Sequence"));
This generates a JOIN similar to this:
SELECT * FROM dbo.Product w
LEFT JOIN dbo.ProductCategory u
ON u.DefaultProductId = w.Id AND u.SubType = 'Premium'
How do I do the same thing with the QueryOver syntax?
Off the top of my head I think it's like:
ProductCategory category = null;
var result = Session.QueryOver<Product>()
.JoinAlias(x => x.Categories, () => category, JoinType.LeftOuterJoin)
.Where(() => category.SubType == "Premium")
.OrderBy(() => category.Sequence).Desc
.Take(1)
.List();
Edit: Included OrderBy, and gave it a test. Works.
Using the Blog > Posts type example, the generated SQL looks like:
SELECT this_.Id as Id1_1_,
this_.Title as Title1_1_,
post1_.BlogId as BlogId3_,
post1_.Id as Id3_,
post1_.Id as Id3_0_,
post1_.Title as Title3_0_,
post1_.Content as Content3_0_,
post1_.DatePosted as DatePosted3_0_,
post1_.BlogId as BlogId3_0_
FROM [Blog] this_
left outer join [Post] post1_
on this_.Id = post1_.BlogId
WHERE post1_.DatePosted > '2011-11-22T19:43:11.00' /* #p0 */
ORDER BY post1_.DatePosted desc
.JoinAlias has an overload with a withClause
var result = Session.QueryOver<Product>()
.Left.JoinAlias(x => x.Categories, () => category, c => c.SubType == "Premium")
.OrderBy(() => category.Sequence).Desc
.Take(1)
.List();
I'm writing part of turist blog so I have two entities: place and photo in relation 1 to many, so every place can have multiple photos, but single photo belongs to one place.
My aim is to create summary: last added 3 places and with every place I want to have one photo (for example photo with the lower id, it doesn't matter). It's important to have not only the id of the photo, but more info (columns) like photo label, path to photo,etc.
Using SQL in MS SQL 2008 it can be done by using rank functions:
select top 3 * from
(
SELECT p.id, p.label, p.VisitDate, ph.id AS photo_id, ph.Label, ph.Path,
RANK() OVER (PARTITION BY p.id ORDER BY ph.id) AS _rank
FROM place AS p INNER JOIN
photo AS ph ON p.id = ph.place_id
) ranked
where _rank = 1
order by VisitDate desc
Or it can be resolved without ranking:
SELECT TOP (3) a.id AS ida, z.id AS idz, z.etykieta, z.sciezka, a.data_odwiedzenia
FROM place AS a INNER JOIN
photo AS z ON a.id = z.id_atrakcji
and z.id in
(
SELECT min(z.id) AS idz
FROM photo z
where z.id_atrakcji in (select top 3 a.id from place a order by a.data_dodania desc)
group by z.id_atrakcji
)
ORDER BY a.data_odwiedzenia DESC
I started writing this query without ranking, but I've stucked.
summaryList =
session.QueryOver(() => placeAlias)
.JoinAlias(o => o.Photos, () => photoAlias)
.SelectList(list => list
.Select(() => placeAlias.Id).WithAlias(() => summaryAlias.PlaceId)
.Select(() => placeAlias.Etykieta).WithAlias(() => summaryAlias.PlaceName)
.Select(() => photoAlias.Etykieta).WithAlias(() => summaryAlias.PhotoLabel)
.Select(() => photoAlias.Id).WithAlias(() => summaryAlias.PhotoId)
)
.OrderByAlias(() => placeAlias.VisitDate).Desc
.WithSubquery.WhereProperty(x => x.Id).In(lastPlaces)
.TransformUsing(Transformers.AliasToBean<PlacesSummary>())
.List<PlacesSummary>();
Can you help me with this? How to write lastPlaces queryover or maybe there's another approach?
Regards,
Macko
var subquery = QueryOver.Of(() => photoAlias)
.Where(photo => photo.Place.Id == placeAlias.Id)
.Select(Projections.Max<Photo>(photo => photo.Id));
var summaryList = session.QueryOver(() => placeAlias)
.OrderBy(() => placeAlias.VisitDate).Desc
.JoinQueryOver(() => placeAlias.Photos, () => photoAlias)
.WithSubquery.WhereProperty(photo => photo.Id).Eq(subquery)
.SelectList(list => list
.Select(() => placeAlias.Id).WithAlias(() => summaryAlias.PlaceId)
.Select(() => placeAlias.Etykieta).WithAlias(() => summaryAlias.PlaceName)
.Select(() => photoAlias.Etykieta).WithAlias(() => summaryAlias.PhotoLabel)
.Select(() => photoAlias.Id).WithAlias(() => summaryAlias.PhotoId)
)
.TransformUsing(Transformers.AliasToBean<PlacesSummary>())
.List<PlacesSummary>();
cant test it right now
it doesn't work as it should but you it is very close:
summaryList =
session.QueryOver(() => placeAlias)
.OrderByAlias(() => placeAlias.VisitDate).Desc
.JoinAlias(o => o.Photos, () => photoAlias)
**.WithSubquery.WhereProperty(()=> photoAlias.Id).Eq(subquery)**
.SelectList(list => list
.Select(() => placeAlias.Id).WithAlias(() => summaryAlias.PlaceId)
.Select(() => placeAlias.Etykieta).WithAlias(() => summaryAlias.PlaceName)
.Select(() => photoAlias.Etykieta).WithAlias(() => summaryAlias.PhotoLabel)
.Select(() => photoAlias.Id).WithAlias(() => summaryAlias.PhotoId))
.TransformUsing(Transformers.AliasToBean<PlacesSummary>())
.Take(3)
.List<PlacesSummary>();
It differs in only one line .WithSubquery where orginally it refers to Place instead it should refer to Photo. Now it's working and it produces following SQL:
SELECT TOP (3) this_.id as y0_, this_.PlaceName as y1_, photoalias1_.PhotoName as y2_, photoalias1_.id as y3_
FROM [place] this_ inner join [photo] photoalias1_ on this_.id=photoalias1_.id_place
WHERE photoalias1_.id = (SELECT min(this_0_.id) as y0_ FROM [photo] this_0_ WHERE this_0_.id_place = this_.id)
ORDER BY this_.DateVisit desc
It's working great but I have another question:
How to rewrite in QueryOver such SQL:
SELECT min(id)
FROM photo
where id in (... it doesn't matter what's here ...)
group by place_id
So I want only list of photo ids (only column) and this query will be used as subquery in another queryover. Can it be done by QueryOver syntax?
I am wondering how do I order a group of results after a select with QueryOver. My query is the following:
CurrentSession.QueryOver<Book>()
.Where(b => b.Author.Name = "SimpleName")
.Select(Projections.Distinct(Projections.Property<Book>(b => b.Genre)))
.OrderBy<Genre>(g => g.Name) // this extension does not exist! How do I order for a Genre?
.List<Genre>()
How can I do?
Your query won't work to begin with. You first of all need to do a join, then you can do your order by and select projections.
Author author = null;
Genre genre = null;
CurrentSession.QueryOver<Book>()
.JoinAlias(b => b.Author, author)
.JoinAlias(b => b.Genre, genre)
.Where(() => author.Name == "SimpleName")
.OrderBy(() => genre.Name)
.Select(Projections.Distinct(Projections.Property<Book>(b => b.Genre)))
.List<Genre>();