spring specification redundant joins - spring

I'm using spring specification to interact with the database, however I can see that in the generated SQL queries there is redundant joins which make the query very slow, here is the code:
Subquery < Mission > usersAccessSubquery = query.subquery(Mission.class);
Root < Mission > usersAccessSubqueryRoot = usersAccessSubquery.from(Mission.class);
usersAccessSubquery.select(usersAccessSubqueryRoot);
Subquery < Mission > groupesAccessSubquery = query.subquery(Mission.class);
Root < Mission > groupesAccessSubqueryRoot = groupesAccessSubquery.from(Mission.class);
groupesAccessSubquery.select(groupesAccessSubqueryRoot);
Subquery < Mission > groupesAccessSubqueryAdmin = query.subquery(Mission.class);
Root < Mission > groupesAccessSubqueryRootAdmin = groupesAccessSubqueryAdmin.from(Mission.class);
groupesAccessSubqueryAdmin.select(groupesAccessSubqueryRootAdmin);
Predicate usersAccessPredicate = cb.equal(cb.lower(usersAccessSubqueryRoot.join("usersAccess").join("userRef").get("email")), authUserMail.toLowerCase());
Predicate groupesAccessPredicate = cb.equal(cb.lower(groupesAccessSubqueryRoot.join("groupesAccess").join("users").join("userRef").get("email")), authUserMail.toLowerCase());
Predicate groupesAccessPredicateAdmin = cb.equal(cb.lower(groupesAccessSubqueryRootAdmin.join("groupe").get("label")), groupe.getLabel().toLowerCase());
Predicate rootPredicate = cb.conjunction();
usersAccessSubquery.where(usersAccessPredicate);
groupesAccessSubquery.where(groupesAccessPredicate);
groupesAccessSubqueryAdmin.where(groupesAccessPredicateAdmin);
query.where(cb.and(
cb.or(
cb.equal(cb.lower(cb.trim(root.get("createdBy"))), authUserMail.toLowerCase().trim()),
cb.in(root.join("mission").get("id")).value(usersAccessSubquery),
cb.in(root.join("mission").get("id")).value(groupesAccessSubquery),
cb.in(root.join("mission").get("id")).value(groupesAccessSubqueryAdmin)
),
rootPredicate
));
and the generated SQL query:
SELECT DISTINCT user0_.id AS id1_25_,
user0_.created_by AS created_2_25_,
user0_.creation_date AS creation3_25_,
user0_.groupe_id AS groupe_i6_25_,
user0_.role_id AS role_id7_25_,
user0_.update_date AS update_d4_25_,
user0_.updated_by AS updated_5_25_,
user0_.user_ref_id AS user_ref8_25_
FROM users user0_
INNER JOIN user_ref userref1_
ON user0_.user_ref_id = userref1_.id
INNER JOIN user_ref userref2_
ON user0_.user_ref_id = userref2_.id
INNER JOIN user_ref userref3_
ON user0_.user_ref_id = userref3_.id
INNER JOIN groupes groupe4_
ON user0_.groupe_id = groupe4_.id
INNER JOIN mission missions5_
ON groupe4_.id = missions5_.client_entity_id
INNER JOIN groupes groupe6_
ON user0_.groupe_id = groupe6_.id
INNER JOIN mission missions7_
ON groupe6_.id = missions7_.client_entity_id
INNER JOIN groupes groupe8_
ON user0_.groupe_id = groupe8_.id
INNER JOIN mission missions9_
ON groupe8_.id = missions9_.client_entity_id
WHERE ( Lower(userref1_.username) LIKE 'a%'
OR Lower(userref2_.first_name) LIKE 'a%'
OR Lower(userref3_.last_name) LIKE 'a%' )
AND ( missions5_.id IN (SELECT mission10_.id
FROM mission mission10_
INNER JOIN groupes groupe11_
ON mission10_.groupe_id =
groupe11_.id
WHERE Lower(groupe11_.label) =
'tls team')
OR missions7_.id IN (SELECT mission12_.id
FROM mission mission12_
INNER JOIN mission_users_access
usersacces13_
ON mission12_.id =
usersacces13_.mission_id
INNER JOIN users user14_
ON
usersacces13_.users_access_id =
user14_.id
INNER JOIN user_ref userref15_
ON user14_.user_ref_id =
userref15_.id
WHERE Lower(userref15_.departement) =
'test#test.com')
OR missions9_.id IN (SELECT mission16_.id
FROM mission mission16_
INNER JOIN mission_groupes_access
groupesacc17_
ON mission16_.id =
groupesacc17_.mission_id
INNER JOIN groupes groupe18_
ON
groupesacc17_.groupes_access_id =
groupe18_.id
INNER JOIN users users19_
ON groupe18_.id =
users19_.groupe_id
INNER JOIN user_ref userref20_
ON users19_.user_ref_id =
userref20_.id
WHERE Lower(userref20_.departement) =
'test#test.com') )
here's the redundant joins:
INNER JOIN user_ref userref1_
ON user0_.user_ref_id = userref1_.id
INNER JOIN user_ref userref2_
ON user0_.user_ref_id = userref2_.id
INNER JOIN user_ref userref3_
ON user0_.user_ref_id = userref3_.id
INNER JOIN groupes groupe4_
ON user0_.groupe_id = groupe4_.id
INNER JOIN mission missions5_
ON groupe4_.id = missions5_.client_entity_id
INNER JOIN groupes groupe6_
ON user0_.groupe_id = groupe6_.id
INNER JOIN mission missions7_
ON groupe6_.id = missions7_.client_entity_id
INNER JOIN groupes groupe8_
ON user0_.groupe_id = groupe8_.id
INNER JOIN mission missions9_
ON groupe8_.id = missions9_.client_entity_id
That can replaced by this:
INNER JOIN user_ref userref1_
ON user0_.user_ref_id = userref1_.id
INNER JOIN groupes groupe4_
ON user0_.groupe_id = groupe4_.id
INNER JOIN mission missions5_
ON groupe4_.id = missions5_.client_entity_id
Which makes execution time much faster, I think the request in the code is optimized, and I don't know why the generated query looks like that, can anybody explain please?

Try to reuse the path, like
Path<?> path = root.join("mission").get("id"));
query.where(cb.and(
cb.or(
cb.equal(cb.lower(cb.trim(root.get("createdBy"))), authUserMail.toLowerCase().trim()),
cb.in(path).value(usersAccessSubquery),
cb.in(path).value(groupesAccessSubquery),
cb.in(path).value(groupesAccessSubqueryAdmin)
),

Related

How can I convert this query from SQL to linq

Can anyone help me converting this SQL query to linq:
select fi.CreatedOn, fi.Id
from Branch br
inner join Asset a on br.Id = a.BranchId
inner join Inspection i on i.AssetId = a.Id
inner join FormInstance fi on i.FormInstanceId = fi.Id
inner join [User] u on u.HomeBranchId = br.Id
where br.Id like 'xxx'
group by fi.CreatedOn, fi.Id
order by fi.CreatedOn desc
This is what I got so far, but I don't understand how to do the group by part
from fi in model.FormInstance
join ins in model.Inspections on fi.Id equals ins.FormInstanceId
join a in model.Assets on ins.AssetId equals a.Id
join br in model.Branches on a.BranchId equals br.Id
join usr in model.Users on br.Id equals usr.HomeBranchId
where br.Id == gBranchId
group fi by fi.Id
You have missed defining right grouping key.
query =
from fi in model.FormInstance
join ins in model.Inspections on fi.Id equals ins.FormInstanceId
join a in model.Assets on ins.AssetId equals a.Id
join br in model.Branches on a.BranchId equals br.Id
join usr in model.Users on br.Id equals usr.HomeBranchId
where br.Id == gBranchId
group fi by new { fi.CreatedOn, fi.Id } into g
select g.Key;
query = query.OrderByDescending(x => x.CreatedOn);

LINQ - Subquery or LEFT OUTER JOIN?

I am trying to optimize my LINQ query performance, and I've noticed a lot of LEFT OUTER JOINs being generated. I know that in SQL, there are some cases in which a single row subquery works better than the equivalent LEFT OUTER JOIN.
For example:
Query 1:
select f.FacilityName, p.Id PatientId, u.DOB, (select u2.NameComputed from adm.Staffs s inner join dbo.AspNetUsers u2 on s.UserId = u2.Id where s.Id = p.StaffId) AssignedTo
from ptn.Patients p
inner join dbo.AspNetUsers u on p.UserId = u.Id
inner join hco.Hcos f on p.HcoId = f.Id
where p.IsRemoved = 0
Query 2:
select f.FacilityName, p.Id PatientId, u.DOB, u2.NameComputed
from ptn.Patients p
inner join dbo.AspNetUsers u on p.UserId = u.Id
inner join hco.Hcos f on p.HcoId = f.Id
left outer join adm.Staffs s on p.StaffId = s.Id
left outer join dbo.AspNetUsers u2 on s.UserId = u2.Id
where p.IsRemoved = 0
Query 1 takes less than a second. Query 2 takes about 45 seconds. If I wanted to make sure LINQ is structured in such a way as to take advantage of this in some cases, how would I go about doing that? That is, is there a way to write a LINQ statement that produces a subquery instead of a LEFT OUTER JOIN?

How to LEFT OUTER JOIN on specific column values in HIVEQL?

When I run the following HiveQL code I get the error:
Execute error: Error while compiling statement: FAILED: SemanticException [Error 10004]: Line 1:2112 Invalid table alias or
column reference 'T3'
SELECT *
FROM CC_CLAIM_EXT T1
INNER JOIN CC_EXPOSURE_EXT T2 ON (T1.ID = T2.CLAIMID)
LEFT OUTER JOIN CC_POLICY_EXT T3 ON (T1.POLICYID = T3.ID)
LEFT OUTER JOIN CC_COVERAGE_EXT T4 ON (T2.COVERAGEID = T4.ID)
LEFT OUTER JOIN CC_TRANSACTION_EXT T5 ON (T2.ID = T5.EXPOSUREID)
LEFT OUTER JOIN CC_TRANSACTIONSET_EXT T6 ON (T5.TRANSACTIONSETID = T6.ID)
LEFT OUTER JOIN CC_TRANSACTIONLINEITEM_EXT T7 ON (T5.ID = T7.TRANSACTIONID)
LEFT OUTER JOIN CC_RISKUNIT_EXT T12 ON (T4.RISKUNITID = T12.ID)
LEFT OUTER JOIN CC_CLASSCODE_EXT T13 ON (T12.CLASSCODEID = T13.ID)
LEFT OUTER JOIN (SELECT TT12.CLAIMID
,CASE WHEN COUNT(TT13.PRIMARYBODYPART) > 1 THEN 10010 ELSE MAX(TT13.PRIMARYBODYPART) END AS PRIMARYBODYPART
,CASE WHEN COUNT(TT13.DETAILEDBODYPART) > 1 THEN 10010 ELSE MAX(TT13.DETAILEDBODYPART) END AS DETAILEDBODYPART
FROM CC_INCIDENT_EXT TT12
LEFT OUTER JOIN CC_BODYPART_EXT TT13 ON (TT12.ID = TT13.INCIDENTID)
GROUP BY TT12.CLAIMID) T14
ON (T1.ID = T14.CLAIMID AND T3.POLICYTYPE IN(10022,10023))
WHERE T1.STATE IN(2,3)
AND T2.STATE IN(2,3)
AND T6.APPROVALSTATUS = 1
AND T7.RETIRED = 0
ORDER BY CLAIMNUMBER
,EXPOSUREID
,TRANSACTIONID
I've narrowed it down to the line:
ON (T1.ID = T14.CLAIMID AND T3.POLICYTYPE IN(10022,10023))
If I delete:
AND T3.POLICYTYPE IN(10022,10023)
The code runs fine. Is there a better way to limit this join in HiveQL?
The error is because you put a reference on the left join between T1 and T14 BUT you put T3 on the condition. To limit your query to your specified T3.policy_id, you should put in along the line where you left join T1 and T3. See below in line 4:
SELECT *
FROM CC_CLAIM_EXT T1
INNER JOIN CC_EXPOSURE_EXT T2 ON (T1.ID = T2.CLAIMID)
LEFT OUTER JOIN CC_POLICY_EXT T3 ON (T1.POLICYID = T3.ID AND T3.POLICYTYPE IN(10022,10023))
LEFT OUTER JOIN CC_COVERAGE_EXT T4 ON (T2.COVERAGEID = T4.ID)
LEFT OUTER JOIN CC_TRANSACTION_EXT T5 ON (T2.ID = T5.EXPOSUREID)
LEFT OUTER JOIN CC_TRANSACTIONSET_EXT T6 ON (T5.TRANSACTIONSETID = T6.ID)
LEFT OUTER JOIN CC_TRANSACTIONLINEITEM_EXT T7 ON (T5.ID = T7.TRANSACTIONID)
LEFT OUTER JOIN CC_RISKUNIT_EXT T12 ON (T4.RISKUNITID = T12.ID)
LEFT OUTER JOIN CC_CLASSCODE_EXT T13 ON (T12.CLASSCODEID = T13.ID)
LEFT OUTER JOIN (SELECT TT12.CLAIMID
,CASE WHEN COUNT(TT13.PRIMARYBODYPART) > 1 THEN 10010 ELSE MAX(TT13.PRIMARYBODYPART) END AS PRIMARYBODYPART
,CASE WHEN COUNT(TT13.DETAILEDBODYPART) > 1 THEN 10010 ELSE MAX(TT13.DETAILEDBODYPART) END AS DETAILEDBODYPART
FROM CC_INCIDENT_EXT TT12
LEFT OUTER JOIN CC_BODYPART_EXT TT13 ON (TT12.ID = TT13.INCIDENTID)
GROUP BY TT12.CLAIMID) T14
ON T1.ID = T14.CLAIMID
WHERE T1.STATE IN(2,3)
AND T2.STATE IN(2,3)
AND T6.APPROVALSTATUS = 1
AND T7.RETIRED = 0
ORDER BY CLAIMNUMBER
,EXPOSUREID
,TRANSACTIONID

Doing a Left outer join in a linq query

I have the query below that works in sqllite with a left outer join
select * from customer cu
inner join contract cnt on cu.customerId = cnt.customerid
inner join address addy on cu.addressid = addy.addressId
inner join csrAssoc cassc on cu.customerid = cassc.customerId
left outer join CustomerServiceRepresentative csrr on cassc.csrid = csrr.customerservicerepresentativeId
inner join customerServiceManager csmm on cassc.csmid = csmm.customerservicemanagerId
where cu.customernumber = '22222234'
I want to be able to apply a left outer join on this line in the linq query below
join csrr in objCsrCustServRep.AsEnumerable() on cassc.CsrId equals
csrr.CustomerServiceRepresentativeId
VisitRepData = (from cu in objCustomer.AsEnumerable()
join cnt in objContract.AsEnumerable() on cu.customerId equals cnt.customerId
join addy in objAddress.AsEnumerable() on cu.addressId equals addy.addressId
join cassc in objCsrAssoc.AsEnumerable() on cu.customerId equals cassc.CustomerId
join csrr in objCsrCustServRep.AsEnumerable() on cassc.CsrId equals
csrr.CustomerServiceRepresentativeId
join csmm in objCustServMan on cassc.CsmId.ToString() equals csmm.customerServiceManagerId
where cu.CustomerNumber == (customernbr)
How can I do a left outer join in a linq query?
Here is my comment after adjusting and running the code. The other section is also added. All am getting is object is not set to an instance of an object.
var VisitRepData = from cu in objCustomer.AsEnumerable()
join cnt in objContract.AsEnumerable() on cu.customerId equals cnt.customerId
join addy in objAddress.AsEnumerable() on cu.addressId equals addy.addressId
join cassc in objCsrAssoc.AsEnumerable() on cu.customerId equals cassc.CustomerId
join csrr in objCsrCustServRep.AsEnumerable() on cassc.CsrId equals
csrr.CustomerServiceRepresentativeId into temp
from tempItem in temp.DefaultIfEmpty()
join csmm in objCustServMan on cassc.CsmId.ToString() equals csmm.customerServiceManagerId
where cu.CustomerNumber == (customernbr)
select new
{
cu.customerId,
cu.CustomerNumber,
cu.customerName,
cu.dateActive,
cnt.contractExpirationDate,
addy.street,
addy.street2,
addy.city,
addy.state,
addy.zipcode,
cu.EMail,
cu.phoneNo,
cu.faxNumber,
csmm.customerServiceManagerName,
tempItem.CustomerServiceRepresentativeName,
};
foreach (var item in VisitRepData)
{
var one = item.customerId;
var two = item.CustomerNumber;
}
Per documentation:
A left outer join is a join in which each element of the first collection is returned, regardless of whether it has any correlated elements in the second collection. You can use LINQ to perform a left outer join by calling the DefaultIfEmpty method on the results of a group join
(emphasis mine)
Based on MSDN link above, and if I understood your requirements correctly, query should look like this:
VisitRepData = from cu in objCustomer.AsEnumerable()
join cnt in objContract.AsEnumerable() on cu.customerId equals cnt.customerId
join addy in objAddress.AsEnumerable() on cu.addressId equals addy.addressId
join cassc in objCsrAssoc.AsEnumerable() on cu.customerId equals cassc.CustomerId
join csrr in objCsrCustServRep.AsEnumerable() on cassc.CsrId equals
csrr.CustomerServiceRepresentativeId into temp
from tempItem in temp.DefaultIfEmpty()
join csmm in objCustServMan on cassc.CsmId.ToString() equals csmm.customerServiceManagerId
where cu.CustomerNumber == (customernbr)
Specifically, the left outer join is performed with this code:
join csrr in objCsrCustServRep.AsEnumerable()
on cassc.CsrId equals csrr.CustomerServiceRepresentativeId
into temp
from tempItem in temp.DefaultIfEmpty()

LEFT OUTER JOIN WHEN IT HAS MULTIPLE TABLE SELECT QUERY

Currently I have joined two tables using inner join , like following
SELECT A.*,B.*
FROM A,B
WHERE A.COLUMN_A = B.COLUMN_B
now I want to join Left outer join to above results , lets say I want to join Table C
So I did like following
SELECT A.*,B.*
FROM A,B
LEFT OUTER JOIN C ON B.COLUMN_X = C.COLUMN_X
WHERE A.COLUMN_A = B.COLUMN_B
this is executing without errors in SQL navigator, But in this result I cannot see any output.
anything wrong in this query , please advise
Change it to have proper join syntax like
SELECT A.*,B.*
FROM A
INNER JOIN B ON A.COLUMN_A = B.COLUMN_B
LEFT OUTER JOIN C ON B.COLUMN_X = C.COLUMN_X;
Better change all to outer join
SELECT A.*,B.*
FROM A
LEFT JOIN B ON A.COLUMN_A = B.COLUMN_B
LEFT OUTER JOIN C ON B.COLUMN_X = C.COLUMN_X;
Use this
SELECT A.*,B.*,C.*
FROM A
INNER JOIN B
ON A.COLUMN_A = B.COLUMN_B
LEFT OUTER JOIN C
ON B.COLUMN_X = C.COLUMN_X
If you absolutely have to use legacy syntax, then use this. But I won't recommend it.
SELECT A.*,B.*,C.*
FROM A,B,C
where A.COLUMN_A = B.COLUMN_B
AND
B.COLUMN_X = C.COLUMN_X (+)

Resources