Not sure how to do this in Fluent NHibernate (preferably) or with Criteria.
SELECT foo.One, foo.Two, bar.One
FROM Bar bar
RIGHT JOIN ( SELECT
One, Two
WHERE One LIKE '%number%'
ORDER BY Id ASC
OFFSET 0 ROWS
FETCH NEXT 25 ROWS ONLY
) Foo
ON Bar.Foo_Id = Foo.Id
WHERE Bar.Two IN (...)
NHibernate criteria, represents a language for querying on top of the Entities, i.e. on top of the mapping. That means, that we (users of NHiernate criteria) can effect the final SQL SELECT statement in these sections:
SELECT (either all mapped columns of the entity, or projections - just a specific columns)
WHERE (any kind of restriction is available, including custom/DB specific statements)
GROUP BY
ORDER BY
HAVING
The fact, that we do query Entities (i.e. how we mapped <class name="MyEntity" table="MyTable"), we can effect the FROM only inside of the mapping.
About the JOIN: we can also decide what will be joined (which mapped reference) and even the join type (LEFT, INNER...). But the ON statement must contain NHibernate generated part, coming from the mapping.
So, the above was namely stated just to get to final statement:
If we want to have a SELECT as part of a FROM/JOIN ... that SELECT must be mapped as some Entity
<class name="MyEntity"... >
<subselect>
SELECT ...
FROM ...
</subselect>
Read more here:
NHibernate Criteria: Subquery After 'From' Clause
Related
I am using a recursive with statement to select all child from a given parent in a table representing tree structured entries. This is in Sqlite (which now supports recursive with).
This allows me to select very quickly thousands of record in this tree whithout suffering the huge performance loss due to preparing thousands of select statements from the calling application.
WITH RECURSIVE q(Id) AS
(
SELECT Id FROM Entity
WHERE Parent=(?)
UNION ALL
SELECT m.Id FROM Entity AS m
JOIN Entity ON m.Id=q.Parent
)
SELECT Id FROM q;
Now, suppose I have related data to these entities in an arbitrary number of other tables, that I want to subsequently load. Due to the arbitrary number of them (in a modular fashion) it is not possible to include the data fetching directly in this one. They must follow it.
But, if for each related tables I then do a SELECT statement, all the performance gain from selecting all the data from the tree directly inside Sqlite is almost useless because I will still stall on thousands of subsequent requests which will each prepare and issue a select statement.
So two questions :
The better solution is to formulate a similar recursive statement for each of the related tables, that will recursively gather the entities from this tree again, and this time select their related data by joining it.
This sounds really more efficient, but it's really tricky to formulate such a statement and I'm a bit lost here.
Now the real mystery is, would there be an even more efficient solution, which would be to somehow keep these results from the last query cached somewhere (the rows with the ids from the entity tree) and join them to the related tables in the following statement without having to recursively iterate over it again ?
Here is a try at the first option, supposing I want to select a field Data from related table Component : is the second UNION ALL legal ?
WITH RECURSIVE q(Data) AS
(
SELECT Id FROM Entity
WHERE Parent=(?)
UNION ALL
SELECT m.Id FROM Entity AS m
JOIN Entity ON m.Id=q.Parent
UNION ALL
SELECT Data FROM Component AS c
JOIN Component ON c.Id=q.Id
)
SELECT Data FROM q;
The documentation says:
2. The table named on the left-hand side of the AS keyword must appear exactly once in the FROM clause of the right-most SELECT statement of the compound select, and nowhere else.
So your second query is not legal.
However, the CTE behaves like a normal table/view, so you can just join it to the related table:
WITH RECURSIVE q(Id) AS
( ... )
SELECT q.Id, c.Data
FROM q JOIN Component AS c ON q.Id = c.Id
If you want to reuse the computed values in q for multiple queries, there's nothing you can do with CTEs, but you can store them in a temporary table:
CREATE TEMPORARY TABLE q_123 AS
WITH RECURSIVE q(Id) AS
( ... )
SELECT Id FROM q;
SELECT * FROM q_123 JOIN Component ...;
SELECT * FROM q_123 JOIN Whatever ...;
DROP TABLE q_123;
How can I create the following SQL statement in Nhibernate using CreateCriteria:
SELECT distinct top 20 a.* from ActivityLog a
left join WallPost w on a.ActivityLogId = w.ActivityLogId left join ItemStatus i on i.StatusId = w.ItemStatus
I always tend to get all columns from all tables returned in the sql statement producing duplicates even though I map it to the ActivityLog table. I am also doing paging as the code below shows:
ICriteria crit = nhelper.NHibernateSession.CreateCriteria(typeof(Model.ActivityLog), "a").CreateAlias("a.WallPosts", "w",CriteriaSpecification.LeftJoin)
.CreateAlias("w.ItemStatus", "i", CriteriaSpecification.LeftJoin)
.SetMaxResults(pageSize).SetFirstResult(startRow).AddOrder(Order.Desc("a.Date"));
Thanks
H
Sounds like you have set lazy loading to false in your mapping files meaning that all the associations and child collections are being loaded up too. Can you verify that?
You ask "How to select one table using NHibernate CreateQuery" (HQL). In that case you can choose what to fetch using select.
In your text you're using criteria. AFAIK, you cannot directly control what columns you want to read. However, if you create a DetachedCriteria for the join, this won't be fetched.
This LINQ to SQL query
From g In Db.Context.Current.Groups
Select g.GroupID
generates this SQL:
SELECT [t0].[GroupID]
FROM [dbo].[Groups] AS [t0]
But this query
From g In Db.Context.Current.Groups
Select g.GroupID, g.MemberCount
generates this SQL:
SELECT
[t0].[GroupID], [t0].[Title], [t0].[Description], ...
-- 24 more fields - omitted for brevity
FROM [dbo].[Groups] AS [t0]
g.MemberCount is a property of the Group class that returns an integer.
Public ReadOnly Property MemberCount() As Integer
Get
Return (
From cgx In KN.Db.Context.Current.ContactsGroupsXtabs
Where cgx.GroupID = Me.GroupID
Select cgx.ContactID
).Count()
End Get
End Property
I very much want to select only the fields I need. How can I persuade LINQ to SQL not to select all columns?
In your first query, LINQ to SQL knows it only needs to return a scalar value GroupID (or a GroupID array). So that's what it does: LINQ to SQL queries only that column in the database and returns only those value(s).
From g In Db.Context.Current.Groups
Select g.GroupID
In your second query, LINQ to SQL knows it needs to return a scalar (same as the first query), plus the value returned by a property/method call on an actual entity instance, a Group object. So, to be able to call MemberCount() there must first be a Group object to call it on, right? So LINQ to SQL must do a full entity fetch, and that's why it's querying all the columns.
From g In Db.Context.Current.Groups
Select g.GroupID, g.MemberCount 'property/method call on an entity
[Just theorizing here...] You might say, But MemberCount() doesn't need a full entity fetch, it doesn't need all that data - all it needs is GroupID. And I would reply, How does LINQ to SQL know that? How can LINQ to SQL know that your method MemberCount() doesn't rely on the data of other db-column-based properties? It can't know that, so it must return the full object. [End theorizing.]
Suggestion: Join ContactsGroupsXtabs in your second query, group on GroupID and do a Count() on the group to get your value. (I can see by your other SO questions and answers that you know how to do all that so I'll skip the example.)
I have a query I've written with NHibernate's Criteria functionality and I want to optimize it. The query joins 4 tables. The query works, but the generated SQL is returning all the columns for the 4 tables as opposed to just the information I want to return. I'm using SetResultTransformer on the query which shapes the returned data to an Individual, but not until after the larger sql is returned from the server.
Here's the NHibernate Criteria
return session.CreateCriteria(typeof(Individual))
.CreateAlias("ExternalIdentifiers", "ExternalIdentifier")
.CreateAlias("ExternalIdentifier.ExternalIdentifierType", "ExternalIdentifierType")
.CreateAlias("ExternalIdentifierType.DataSource", "Datasource")
.Add(Restrictions.Eq("ExternalIdentifier.Text1", ExternalId))
.Add(Restrictions.Eq("ExternalIdentifierType.Code", ExternalIdType))
.Add(Restrictions.Eq("Datasource.Code", DataSourceCode))
.SetResultTransformer(new NHibernate.Transform.RootEntityResultTransformer());
And the generated sql (from NHProfiler) is
SELECT (all columns from all joined tables)
FROM INDIVIDUAL this_
inner join EXTERNAL_ID externalid1_
on this_.INDIVIDUAL_GUID = externalid1_.GENERIC_GUID
inner join EXTERNAL_ID_TYPE externalid2_
on externalid1_.EXTERNAL_ID_TYPE_GUID = externalid2_.EXTERNAL_ID_TYPE_GUID
inner join SYSTEM_SRC datasource3_
on externalid2_.SYSTEM_SRC_GUID = datasource3_.SYSTEM_SRC_GUID
WHERE externalid1_.EXTERNAL_ID_TEXT_1 = 96800 /* #p0 */
and externalid2_.EXTERNAL_ID_TYPE_CODE = 'PATIENT' /* #p1 */
and datasource3_.SYSTEM_SRC_CODE = 'TOUCHPOINT' /* #p2 */
I only want the columns back from the Individual table. I could set a projection, but then I lose the Individual type.
I could also rewrite this with DetachedCriteria.
Are these my only options?
I had exactly the same question and asked one of NHibernate development team members directly, here is the answer I got:
with the Criteria API, the only thing you could do is to use a projection list and include all of the properties of the root entity... then you would need to use the AliasToBeanResultTransformer. Far from an optimal solution obviously
if you don't mind rewriting the query with hql, then you can do it very easily. an hql query that says "from MyEntity e join e.Association" will select both the entity columns as well as the association's columns (just like the problem you're having with criteria). But an hql query that says "select e from MyEntity e join e.Association" will only select the columns of e.
I have an entity type A. Which has many B's. The B entity has many C's.
I need to count how many C's does an A entity have. How can this be done using NHibernate Criteria API?
Using LINQ to NHibernate I was unable to get results since it throws an exception (see this question)
This query becomes simpler if you use C as starting point of the query, rather than A. This is possible since you use bidirectional mappings, according to the mapping you show in your other question.
The way I would do this is to find all Cs which have a B which has a given A, and then count the found Cs.
To Add a constraint on the B of a C, you can add an alias and then add restrictions to that alias. To perform a count query rather than returning the found Cs you can use the SetProjection method and specify a Count projection. Since the count projection returns a single integer value, use UniqueResult to get the count.
using (ISession session = SessionFactorySingleton.OpenSession())
{
int numberOfCsForA1 = session.CreateCriteria<C>()
.SetProjection(Projections.Count("Id"))
.CreateAlias("B", "b")
.Add(Restrictions.Eq("b.A.Id", a1.Id))
.UniqueResult<int>();
// ...
}
The SQL generated by this query looks like this:
SELECT count(this_.Id) as y0_
FROM [C] this_
inner join [B] b1_
on this_.IdB=b1_.Id
WHERE b1_.IdA = #p0;#p0 = 12
As you can see, it is a two-way join, since NHibernate is smart enough to realize that it does not need to join with the A table to get the id of a Bs A. Instead it simply looks at the IdA value of the B.