How do i write below query in Grails either using criteria query or executeQuery?
select * from table1 as t1 left Join(select * from table2 where id=2)as t2 On t2.table1=t1.id ;
Unlike SQL, in which you query tables, GORM/Hibernate queries query domain classes. Yes, it boils down to tables, but from the Criteria, WHERE, and HQL point-of-view, it's domain classes. Which is why, as Koloritnij pointed out, knowledge of the domain model is necessary for writing the query.
SQL vs GORM
One difference in how SQL and GORM perform joins is that SQL joins are created on-the-fly, in the SQL itself. Whereas GORM joins are predetermined by the domain class associations. This means that in HQL you cannot join to a sub-query. You can read more about such differences here.
An example
That being said, using your example SQL I made an assumption about your domain model:
class DomainA {
}
class DomainB {
DomainA a
}
In the domain model above, DomainB (table2) has a uni-directional many-to-one association with DomainA (table1). An HQL similar to your SQL is as follows:
def hql = 'SELECT a, b FROM DomainB AS b RIGHT OUTER JOIN b.a AS a WHERE b.id = :id'
The HQL can be executed like this:
def result = DomainB.executeQuery(hql, [id: 2])
Here you go (if you have Domain Classes with foreign key):
def query
query = sessionFactory.getCurrentSession().createCriteria(table2.class)
query = query.createAlias("table1", "table1Alias", CriteriaSpecification.LEFT_JOIN, Restrictions.in( 'table1Alias.id', 'table2.id'))
Restrictions.eq( "id", 2)
If not, you have to use raw SQL:
def dataSource
Sql sql = new Sql(dataSource)
sql.exequteQuery("""....""")
Related
I have a query written using Predicates in Hibernate and I need to add a subquery on a join table to count the number of joins and order by the number of joins where they exist in an array of ids.
The join table is a ManyToMany relation.
I am using flyway to setup the table schema, so while the join table exists in the database, a join model is not needed in Hibernate to join the 2 related models therefore no join model exists.
I don't care about retrieving these related models, I just want the number of joins so that I can order by them.
The following is PostGreSQL, which works. I need to convert the following PSQL into a Predicate based query:
SELECT u.*, COUNT(jui.interest_id) AS juiCount
FROM "user" u
LEFT JOIN (
SELECT ui.user_id, ui.interest_id
FROM user_interest ui
WHERE ui.interest_id IN (?)
) AS jui ON u.id = jui.user_id
GROUP BY u.id
ORDER BY juiCount DESC
Where the ids provided for the IN condition are passed into the subquery. The above query is in PostGreSQL.
Working with what I have so far:
CriteriaBuilder b = em.getCriteriaBuilder();
CriteriaQuery<User> q = b.createQuery(User.class);
Root<User> u = q.from(User.class);
// This doesn't make sense because this is not a join table
// Subquery<Interest> sq = q.subquery(Interest.class)
// Root<Interest> squi = sq.from(Interest.class);
// sq.select(squi);
// sq.where(b.in("interest_id", new LiteralExpression<Long[]>((CriteriaBuilderImpl) b, interestIds)));
q.orderBy(
// b.desc(b.tuple(u, b.count(squi))),
b.asc(u.get(User_.id))
);
q.where(p);
return em
.createQuery(q)
.getResultList();
Everything I have managed to find doesn't quite seem to fit right given that they are not using ManyToMany in the use of q.subquery() in their example.
Anyone can help fill in the blanks on this please?
I have a collection of IDs in memory, and I would like to fetch only rows from a DB matching those IDs.
In SQL, I could either write a query like SELECT * FROM mytable WHERE id IN (1,3,5,10) or do a join between tables.
My problem is that EF can't build a query where I join my EF-data with my local array or list.
(I'm using EF4.1, but I'm guessing the problem/solution would be similar in older versions, as well as with Linq-to-SQL.)
You can use Contains() with your ID collection myIDs to generate the equivalent WHERE id IN .. query:
var results = context.mytable.Where(x => myIds.Contains(x.Id));
I have two entities: Master and Details.
When I query them, the resulting query to database is:
SELECT [Extent2]."needed columns listed here", [Extent1]."needed columns listed here"
FROM (SELECT * [Details]."all columns listed here"...
FROM [dbo].[Details] AS [Details]) AS [Extent1]
LEFT OUTER JOIN [dbo].[Master] AS [Extent2] ON [Extent1].[key] = [Extent2].[key]
WHERE [Extent1].[filterColumn] = #p__linq__0
My question is: why not the filter is in the inner query? How can I get this query? I've tried a lot of EF and Linq expressions.
What I need is something like:
SELECT <anything needed>
FROM Master LEFT JOIN Details ON Master.key = Details.Key
WHERE filterColumn = #param
I'm having a full sequential scan in both tables, and in my production environment, I have milions of rows in each table.
Thanks a lot !!
Sometimes The entity Framework does not produce the best query. You can do a few of the following to optimize.
Modify the linq statement (test with
LINQPad)
Create a stored proc and map the stored proc to return an entity
Create a view that handles the join and map the view to a new
entity
I want to achieve the following in Linq to Entities:
Get all Enquires that have no Application or the Application has a status != 4 (Completed)
select e.*
from Enquiry enq
left outer join Application app
on enq.enquiryid = app.enquiryid
where app.Status <> 4 or app.enquiryid is null
Has anyone done this before without using DefaultIfEmpty(), which is not supported by Linq to Entities?
I'm trying to add a filter to an IQueryable query like this:
IQueryable<Enquiry> query = Context.EnquirySet;
query = (from e in query
where e.Applications.DefaultIfEmpty()
.Where(app=>app.Status != 4).Count() >= 1
select e);
Thanks
Mark
In EF 4.0+, LEFT JOIN syntax is a little different and presents a crazy quirk:
var query = from c1 in db.Category
join c2 in db.Category on c1.CategoryID equals c2.ParentCategoryID
into ChildCategory
from cc in ChildCategory.DefaultIfEmpty()
select new CategoryObject
{
CategoryID = c1.CategoryID,
ChildName = cc.CategoryName
}
If you capture the execution of this query in SQL Server Profiler, you will see that it does indeed perform a LEFT OUTER JOIN. HOWEVER, if you have multiple LEFT JOIN ("Group Join") clauses in your Linq-to-Entity query, I have found that the self-join clause MAY actually execute as in INNER JOIN - EVEN IF THE ABOVE SYNTAX IS USED!
The resolution to that? As crazy and, according to MS, wrong as it sounds, I resolved this by changing the order of the join clauses. If the self-referencing LEFT JOIN clause was the 1st Linq Group Join, SQL Profiler reported an INNER JOIN. If the self-referencing LEFT JOIN clause was the LAST Linq Group Join, SQL Profiler reported an LEFT JOIN.
Do this:
IQueryable<Enquiry> query = Context.EnquirySet;
query = (from e in query
where (!e.Applications.Any())
|| e.Applications.Any(app => app.Status != 4)
select e);
I don't find LINQ's handling of the problem of what would be an "outer join" in SQL "goofy" at all. The key to understanding it is to think in terms of an object graph with nullable properties rather than a tabular result set.
Any() maps to EXISTS in SQL, so it's far more efficient than Count() in some cases.
Thanks guys for your help. I went for this option in the end but your solutions have helped broaden my knowledge.
IQueryable<Enquiry> query = Context.EnquirySet;
query = query.Except(from e in query
from a in e.Applications
where a.Status == 4
select e);
Because of Linq's goofy (read non-standard) way of handling outers, you have to use DefaultIfEmpty().
What you'll do is run your Linq-To-Entities query into two IEnumerables, then LEFT Join them using DefaultIfEmpty(). It may look something like:
IQueryable enq = Enquiry.Select();
IQueryable app = Application.Select();
var x = from e in enq
join a in app on e.enquiryid equals a.enquiryid
into ae
where e.Status != 4
from appEnq in ae.DefaultIfEmpty()
select e.*;
Just because you can't do it with Linq-To-Entities doesn't mean you can't do it with raw Linq.
(Note: before anyone downvotes me ... yes, I know there are more elegant ways to do this. I'm just trying to make it understandable. It's the concept that's important, right?)
Another thing to consider, if you directly reference any properties in your where clause from a left-joined group (using the into syntax) without checking for null, Entity Framework will still convert your LEFT JOIN into an INNER JOIN.
To avoid this, filter on the "from x in leftJoinedExtent" part of your query like so:
var y = from parent in thing
join child in subthing on parent.ID equals child.ParentID into childTemp
from childLJ in childTemp.Where(c => c.Visible == true).DefaultIfEmpty()
where parent.ID == 123
select new {
ParentID = parent.ID,
ChildID = childLJ.ID
};
ChildID in the anonymous type will be a nullable type and the query this generates will be a LEFT JOIN.
Is there any difference between these two LINQ statements:
var query = from a in entities.A
where a.Name == "Something"
from b in entities.B
where a.Id == b.Id
select b;
var query = from a in entities.A
join b in entities.B on a.Id equals b.Id
where a.Name == "Something"
select b;
Are both statements doing an inner join?
Also how do I view generated the generated SQL statement from the Entity Framework?
This doesn't precisely answer your question, but it's nearly always wrong to use join in LINQ to Entities. Both queries are, in my opinion, incorrect. What you actually want to do in this case is:
var query = from a in entities.A
where a.Name == "Something"
from b in a.Bs // where Bs is the name of the relationship to B on A,
select b; // whatever it's called
You already have the specification of the relationship encoded in your DB foreign keys and your entity model. Don't duplicate it in your queries.
You can get, and compare the SQL for, those queries:
((ObjectQuery)query).ToTraceString();
The generated SQL may be (subtly) different depending on how EF interprets those queries.
FYI- You don't have to include joins when querying related entities.
Logically speaking these two statements are doing the same thing. If they are computed differently by the framework then I would be unimpressed.
Take a look to the sql profiler. You could get your answer.