I have the following table structures:
TABLE A:
ID
COL1
COL2
...
COL(n)
TABLE B:
ID
A_ID (id in table A)
VALUE
There is a one-to-many relationship from A->B
class A {
int id
...
coln
Set<String> bSet
static hasMany = [bSet: B]
static mapping = {
restrictions joinTable: [name: "B", key: "A_ID", column: "VALUE"]
}
}
How can I build a criteria so that it executes a query such as follows:
select table1.* from A table1 where (select count(*) from B table2 where table2.A_ID = table1.ID and table2.VALUE in ('excluded_value_1','excluded_value_2')) = 0
Give this a try, obviously untested but might give you something to start from
def a = A.createCriteria()
a.list {
createAlias("bSet", "b", CriteriaSpecification.LEFT_JOIN)
not {
'in'("b",['excluded_value_1','excluded_value_2'])
}
}
Related
I am new to grails framework how to query 3 tables having association between them
Class A{
static hasMany = [b:B]
}
enter code here
class B{
long aId // Id of table A
}
class c{
B b //B reference
}
SQL query: select * from C where b_id in (select id from B where a_id='10'_)
Any help will be appreciated.
Straight-forward
def list = C.withCriteria{
b{
eq 'aId', '10'
}
}
Starting with the following Domains:
class Person {
String login
String name
}
class MyDomain {
Person creator
static hasMany = [userlist:Person]
}
I wrote a critera to retreive all MyDomainIntances where I'm as creator OR where I'm in the userlist:
def login = "myself"
def result = MyDomain.createCriteria().list () {
projections { distinct ( "id" )
property("name")
property("id")
}
or {
userlist{eq("login", login)}
creator {eq("login",login)}
}
order("name","desc")
}
The problem is with this code I get only instances where I'm in the userlist.
Though creator {eq("login",login)} works well: if I use it itself only, I get a list where I'm as creator
generated query:
Hibernate:
select distinct this_.id as y0_,this_.name as y1_,this_.id as y2_
from mydomain this_ inner join person creator_al2_ on this_.creator_id=creator_al2_.id
inner join mydomain_person userlist5_ on this_.id=userlist5_.mydomain_userlist_id
inner join person userlist1_ on userlist5_.person_id=userlist1_.id
where ((userlist1_.login=?) or (creator_al2_.login=?))
order by this_.name desc
I think the issue is in the OR, as you state that you only get results where 'myself' is in the user list. This blog may help, which also cites this helpful article on createAlias.
def result = MyDomain.createCriteria().list () {
createAlias('creator', 'c')
projections { distinct ( "id" )
property("name")
property("id")
}
or {
userlist{
eq("login", login)
}
eq("c.login", login)
}
order("name","desc")
}
The question is: how do I make GORM generate left joins instead of inner joins in this particular example?
Testbed:
Given classes A, B and C:
class A{
B someObject
}
class B{
C importantObject
}
class C{
boolean interestingFlag
}
I want to list all the elements of A class that:
their B.C object is null OR
their B.C object interestingFlag value is false
What I tried so far:
This approach produces correct list of A where B.C is null (conditional 2 commented out) OR correct list of A where B.C.interestingFlag = false (no matter if conditional 1 is commented out or not). When both conditionals are uncommented it returns only a list of elements where A.B.C.interestingFlag = false (A.B.C = null conditional is ignored)
// approach 1 (conditional 1 is ignored)
def result = A.withCriteria{
someObject{
or{
isNull('importantObject') // conditional 1, works well when conditional 2 is commented out
importantObject{
eq('interestingFlag', false) // conditional 2, works well alone, discards conditional 1 when both of them are uncommented
}
}
}
}
Edit:
As requested in comment I'm pasting a hibernate generated sql:
Hibernate: select this_.id as id1_2_, this_.version as version1_2_,
this_.some_object_id as some3_1_2_, someobject1_.id as id2_0_,
someobject1_.version as version2_0_, someobject1_.important_object_id as
important3_2_0_, importanto2_.id as id0_1_, importanto2_.version as version0_1_,
importanto2_.interesting_flag as interest3_0_1_ from a this_
inner join b someobject1_ on this_.some_object_id=someobject1_.id
inner join c importanto2_ on someobject1_.important_object_id=importanto2_.id
where ((someobject1_.important_object_id is null or (importanto2_.interesting_flag=?)))
When I copy and paste it in the pgAdmin query tool directly with a few things changed (inner joins changed to left joins, and provided the interestingFlag = "false" parameter) everything works as I wanted (I get both A.B.C = null and A.B.C.importantFlag = false objects)
Hibernate: select this_.id as id1_2_, this_.version as version1_2_,
this_.some_object_id as some3_1_2_, someobject1_.id as id2_0_,
someobject1_.version as version2_0_, someobject1_.important_object_id as
important3_2_0_, importanto2_.id as id0_1_, importanto2_.version as version0_1_,
importanto2_.interesting_flag as interest3_0_1_ from a this_
left join b someobject1_ on this_.some_object_id=someobject1_.id
left join c importanto2_ on someobject1_.important_object_id=importanto2_.id
where ((someobject1_.important_object_id is null or (importanto2_.interesting_flag=false)))
Tested and working solution:
def result = A.withCriteria{
createAlias('someObject', 'so', CriteriaSpecification.LEFT_JOIN)
createAlias('so.importantObject', 'imp', CriteriaSpecification.LEFT_JOIN)
or {
isNull('so.importantObject')
eq('imp.interestingFlag', false)
}
}
Solution update as suggested in comment:
def result = A.withCriteria{
createAlias('someObject', 'so', JoinType.LEFT_OUTER_JOIN)
createAlias('so.importantObject', 'imp', JoinType.LEFT_OUTER_JOIN)
or {
isNull('so.importantObject')
eq('imp.interestingFlag', false)
}
}
Use left join to achieve this. This should work, but I haven't tested it live.
def result = A.withCriteria{
someObject {
createAlias("importantObject", "io", CriteriaSpecification.LEFT_JOIN)
or{
isNull('importantObject') // conditional 1
eq('io.interestingFlag', false) // conditional 2
}
}
}
Eidt: based on this post: http://grails.1312388.n4.nabble.com/CriteriaBuilder-DSL-Enhancements-td4644831.html this should also work:
def result = A.withCriteria{
someObject {
isNull('importantObject') // conditional 1
importantObject(JoinType.LEFT) {
eq('interestingFlag', false) // conditional 2
}
}
}
Please post your results on both solutions.
Edit 2: This is a query that is exactly a situation you've described, but differs from your examples. You must start from here, use showSql to debug generated SQLs and fiddle with left joins.
def result = A.withCriteria{
or {
isNull('someObject')
eq('someObject.importantObject.interestingFlag', false)
}
}
I was able to achieve left outer joins only with HQL
Class Transaction {
String someProperty
static hasMany = [reviews: Review]
static hasOne = [reviewQueue: ReviewQueue]
}
Class ReviewQueue {
Transaction transaction
Boolean isComplete
Boolean isReady
}
Class Review {
Transaction transaction
String reviewResult
}
def list = Transaction.executeQuery(
"select t.id, rq.isReady, rq.isComplete, count(r.transaction.id) " +
"from Transaction t " +
"join t.reviewQueue rq " +
"left outer join t.reviews r " +
"where rq.isComplete = false " +
"and rq.isReady = true " +
"group by t.id " +
"having count(r.transaction.id) = 0 " +
"order by rq.transaction.id ",
[max: 10, offset: 0])
I have domain objects for each table specified in the below query. I'm having trouble creating the withCriteria closure representing the below SQL query. Any thoughts?
Thanks!
Steve
SQL Query:
select A_NAME from A
where A_XID =
(select A_XID from B
where B_XID =
(select distinct B_XID from C
where D_XID = '${d.dXid}')
Domain Objects:
class A {
String aName
BigDecimal aXid <-- unique identifier
}
class B {
A a
BigDecimal bXid <-- unique identifier
}
class C {
D d
B b
}
I'm not sure how to do this with a criteria query, but in HQL it'd be
String aName = A.executeQuery(
'select c.b.a.aName from C c where c.d = :d',
[d: d])[0]
but you've left out a lot of information, so this is based on the assumption that you have these domain classes (you omitted the D class and mappings):
class A {
String aName
BigDecimal aXid
}
class B {
A a
BigDecimal bXid
static mapping = {
a column: 'A_XID'
}
}
class C {
D d
B b
static mapping = {
b column: 'B_XID'
d column: 'D_XID'
}
}
class D {
String someProperty
}
I have a complex query that I'm trying to reproduce in LINQ to Entities, but I'm not there yet - is it possible?
The t-sql query looks like:
select distinct
C.id,
L.id
from
dp
join L on L.fk = DP.id
join M on ( M.l_code = L.l_code and M.dp_code = DP.dp_code )
join C on C.c_code = M.c_code
where not exists ( select id from map where map.cid = c.id and map.lid = l.id )
Tables look like:
DP:
id (pk)
dp_code (varchar)
L:
id (pk)
fk (fk to DP.ID)
l_code (varchar)
M:
id (pk)
l_code (varchar, matches value in L.l_code)
dp_code (varchar, matches value in DP.dp_code)
c_code (varchar, matches the value in C.c_code)
C:
id (pk)
c_code (varchar)
MAP:
id (pk)
cid (fk to C.id)
lid (fk to L.id)
My LINQ looks like:
IQueryable foo = from dp in ctx.DP
from l in dl.L
join m in ctx.M on l.l_code equals m.m_code
// Q1: how can I add: AND m.dp_code = dp.dp_code
join c in ctx.C on m.c_code = c.c_code
// this works, but why can't I do it as an AND in the join?
where m.dp_code == dp.dp_code
select new
{
cid = c.id,
cid = c.id
}.Distinct();
So, questions:
Q1: Why can't I do two conditions in the join? User error, or limitations in LINQ?
Q2: How can I add the NOT EXISTS to the query? I've looked at this question, but can't see how to implement the NOT EXISTS subquery.
You can, but it's usually wrong to do a join at all. Still, if you must, you use anonymous types: on new { l: l.l_code, d: dp.code } equals new { l: m_code, d: m.dp_code }
where !(from m in map where whatever select m).Any(). But as with (1), it's better to use associations.