I have following problem with Linq to entities.
Diagram:
Linq to entities query (incomplete yet):
from a in Anlaesse
join b in Beurteilung on a.AnlassID equals b.AnlassID into ab
from b in ab.DefaultIfEmpty()
join r in Rangliste on a.AnlassID equals r.AnlassId
join p in Pony on r.PonyId equals p.PonyID into rp
from p in rp.DefaultIfEmpty()
where a.AnlassID == 67
select new
{
BeurteilungId = b.BeurteilungID == null ? 0 : b.BeurteilungID,
PonyID = p.PonyID == null ? 0 : p.PonyID,
Name = p.Name,
PonyName1 = r.PonyName1,
AnlassId = a.AnlassID == null ? 0 : a.AnlassID
}
Generated SQL:
SELECT
[Extent1].[AnlassID] AS [AnlassID],
CASE WHEN ([Extent2].[BeurteilungID] IS NULL) THEN 0 ELSE
[Extent2].[BeurteilungID] END AS [C1],
CASE WHEN ([Extent4].[PonyID] IS NULL) THEN 0 ELSE [Extent4].[PonyID] END
AS [C2],
[Extent4].[Name] AS [Name],
[Extent3].[PonyName1] AS [PonyName1]
FROM [sspv].[Anlaesse] AS [Extent1]
LEFT OUTER JOIN [sspv].[Beurteilung] AS [Extent2] ON [Extent1].[AnlassID]
= [Extent2].[AnlassID]
INNER JOIN [sspv].[Rangliste] AS [Extent3] ON [Extent1].[AnlassID] =
[Extent3].[AnlassId]
LEFT OUTER JOIN [sspv].[Pony] AS [Extent4] ON [Extent3].[PonyId] =
[Extent4].[PonyID]
WHERE 67 = [Extent1].[AnlassID]
Problem is that I'm unable to add the left outer join between Pony and Beurteilung because all tables are already "used" in my query. Without this the result is wrong because it Returns for each Pony all Beurteilung.
Let's assume you want to left join Pony to Beurteilung. And let's assume both have a PonyId property. A clause like this would act as an inner join:
where p.PonyId == b.PonyId
A clause like this would act as a left join.
where b == null || p.PonyId == b.PonyId
However, you are finding this hard because it really doesn't look like you are using Entity Framework properly. Have you mapped any navigation properties or collections? If you haven't then add them because this is sort of the point of EF. If you have then you shouldn't have any need to use inner or left joins; you just traverse the navigation properties.
Related
Following is the query
Select
pu.username,
ROLE.ROLE_NAME,
(
CASE
WHEN ((ROLE.ORG_ID IS NOT NULL) AND ROLE.ORG_ID=fabuv.BU_ID) THEN FABUV.BU_NAME
WHEN ((ROLE.LEDGER_ID IS NOT NULL) AND ROLE.LEDGER_ID=gl.LEDGER_ID) THEN GL.NAME
WHEN ((ROLE.BOOK_ID IS NOT NULL) AND ROLE.BOOK_ID=fbc.BOOK_CONTROL_ID) THEN FBC.BOOK_TYPE_NAME
WHEN ((ROLE.SET_ID IS NOT NULL) AND ROLE.SET_ID=fssv.SET_ID) THEN FSSV.SET_NAME
WHEN ((ROLE.INV_ORGANIZATION_ID IS NOT NULL) AND ROLE.INV_ORGANIZATION_ID=iop.ORGANIZATION_ID) THEN IOP.ORGANIZATION_CODE
WHEN ((ROLE.CST_ORGANIZATION_ID IS NOT NULL) AND ROLE.CST_ORGANIZATION_ID=ccov.COST_ORG_ID) THEN CCOV.COST_ORG_CODE
WHEN ((ROLE.ACCESS_SET_ID IS NOT NULL) AND ROLE.ACCESS_SET_ID=gas.ACCESS_SET_ID) THEN GAS.NAME
WHEN ((ROLE.CONTROL_BUDGET_ID IS NOT NULL) AND ROLE.CONTROL_BUDGET_ID=xcb.CONTROL_BUDGET_ID) THEN XCB.NAME
WHEN ((ROLE.INTERCO_ORG_ID IS NOT NULL) AND ROLE.INTERCO_ORG_ID=fio.INTERCO_ORG_ID) THEN FIO.INTERCO_ORG_NAME
ELSE 'NOT_APPLICABLE'
END
) "Security_Context_Value"
from
fusion.per_users pu,
fusion.FA_BOOK_CONTROLS FBC,
fusion.FUN_ALL_BUSINESS_UNITS_V FABUV,
fusion.XCC_CONTROL_BUDGETS XCB,
fusion.CST_COST_ORGS_V CCOV,
fusion.gl_access_sets GAS,
fusion.FUN_INTERCO_ORGANIZATIONS FIO,
fusion.INV_ORG_PARAMETERS iOP,
fusion.GL_LEDGERS GL,
fusion.RCS_MFG_PARAMETERS RMP,
fusion.FUN_USER_ROLE_DATA_ASGNMNTS ROLE,
fusion.FND_SETID_SETS_VL FSSV
where
1=1
and pu.USERNAME=:Username
AND ROLE.ORG_ID != NULL
OR ROLE.LEDGER_ID != NULL
OR ROLE.BOOK_ID != NULL
OR ROLE.SET_ID != NULL
OR ROLE.INV_ORGANIZATION_ID != NULL
OR ROLE.CST_ORGANIZATION_ID != NULL
OR ROLE.ACCESS_SET_ID != NULL
OR ROLE.CONTROL_BUDGET_ID != NULL
OR ROLE.INTERCO_ORG_ID != NULL
And pu.USER_GUID=role.USER_GUID
It is the query required to extract roles assigned to users with Security_Context and Value.
I have tried different combinations of using IS NOT NULL in place of '!=' but it also didnt help, again Username is not taken into consideration when if it run.
I think what you're after is something like:
SELECT pu.username,
role.role_name,
CASE WHEN ROLE.ORG_ID IS NOT NULL THEN FABUV.BU_NAME
WHEN ROLE.LEDGER_ID IS NOT NULL THEN GL.NAME
WHEN ROLE.BOOK_ID IS NOT NULL THEN FBC.BOOK_TYPE_NAME
WHEN ROLE.SET_ID IS NOT NULL THEN FSSV.SET_NAME
WHEN ROLE.INV_ORGANIZATION_ID IS NOT NULL THEN IOP.ORGANIZATION_CODE
WHEN ROLE.CST_ORGANIZATION_ID IS NOT NULL THEN CCOV.COST_ORG_CODE
WHEN ROLE.ACCESS_SET_ID IS NOT NULL THEN GAS.NAME
WHEN ROLE.CONTROL_BUDGET_ID IS NOT NULL THEN XCB.NAME
WHEN ROLE.INTERCO_ORG_ID IS NOT NULL THEN FIO.INTERCO_ORG_NAME
ELSE 'NOT_APPLICABLE'
END "Security_Context_Value"
FROM fusion.per_users pu
INNER JOIN fusion.fun_user_role_data_asgnmnts role ON pu.user_guid = role.user_guid
LEFT OUTER JOIN fusion.fun_all_business_units_v fabuv ON role.org_id = fabuv.bu_id
LEFT OUTER JOIN fusion.gl_ledgers gl ON role.ledger_id = gl.ledger_id
LEFT OUTER JOIN fusion.fa_book_controls fbc ON role.book_id=fbc.book_control_id
LEFT OUTER JOIN fusion.fnd_setid_sets_vl fssv ON role.SET_ID=fssv.SET_ID
LEFT OUTER JOIN fusion.inv_org_parameters iop ON role.inv_organization_id=iop.organization_id
LEFT OUTER JOIN fusion.cst_cost_orgs_v ccov ON role.cst_organization_id=ccov.cost_org_id
LEFT OUTER JOIN fusion.gl_access_sets gas ON role.access_set_id=gas.access_set_id
LEFT OUTER JOIN fusion.xcc_control_budgets xcb ON role.control_budget_id=xcb.control_budget_id
LEFT OUTER JOIN fusion.fun_interco_organizations fio ON role.interco_org_id=fio.interco_org_id
WHERE pu.username = :username
AND COALESCE(role.org_id,
role.ledger_id,
role.book_id,
role.set_id,
role.inv_organization_id,
role.cst_organization_id,
role.access_set_id,
role.control_budget_id,
role.interco_org_id) IS NOT NULL;
Note the use of the ANSI join syntax, which forces you to add in join conditions (unless you explicitly request a cross join).
Your original query was doing a cross join between your fabuv, gl, fbc, etc tables, meaning each row in fabuv was being joined to all rows in gl, and each of the resultant rows was being joined to every row in the fbc table and so on... that's a lot of rows - e.g. if there are 100 rows in each of the fabuv, gl, fbc, etc tables, you're going to end up with 100 x 100 x 100 x ... which ends up being way too many rows.
I've take a guess as to what the join conditions should be, based on your case statement - n.b. the rmp table wasn't being referenced at all, so I've excluded it from the from clause.
I also used the COALESCE function to simplify your where clause - this function returns the first not-null value.
I would like to learn if there is any more efficient way to write the query below:
SELECT *
FROM requests srp
INNER JOIN surgeons rpsur
ON rpsur.id = srp.surgeon_id
LEFT OUTER JOIN #usersurgeons usersurgeons
ON usersurgeons.surgeon_id = srp.surgeon_id
LEFT OUTER JOIN surgeons LOsurgeons
ON usersurgeons.surgeon_id = LOsurgeons.id
LEFT OUTER JOIN provsurgeons LOprovsurgeons
ON LOprovsurgeons.id = LOsurgeons.provsurgeon_id
INNER JOIN #selectedsurgeons up
ON up.surgeon_id = rpsur.id
LEFT OUTER JOIN provsurgeons ps
ON ps.id = rpsur.provsurgeon_id
WHERE rpsur.isprimary = 0
AND usersurgeons.isprimary = 0
AND LOsurgeons.isprimary = 0
AND LOprovsurgeons.isprimary = 0
AND up.isprimary = 0
AND ps.isprimary = 0
I am not happy with the where clause here, is there any more professional way to write this, rather than adding the clauses to the join lines (such as on xx.id = yy.id and xx.isPrimary=0)??
From this query alone there are not many things that can be said. You should consider adding some more context (how do you get data into those temporary tables and the structure of %surgeons tables):
1) Select * makes almost impossible to use any index and also provides a lot of columns (Requests.*, surgeons.*, Provsurgeons.* etc.) in your final result. Return only the columns that you need.
2) If isPrimary = 0 filtering is performed often in your queries (not just this one), you can consider creating a view that fetches data filtered by isPrimary = 0. E.g. vwSurgeons, vwProvsurgeons. Then, you can just JOIN directly to the view instead of the table.
3) [already mentioned in the comments] Any condition that excludes NULL values for the OUTER JOINed table will transform the OUTER into INNER.
Instead of joining all tables and having a where clause at the end, use a derived tables only with filtered records. This way your query performance will be better.
SELECT *
FROM requests srp
INNER JOIN surgeons rpsur
ON rpsur.id = srp.surgeon_id
LEFT OUTER JOIN
(
SELECT *
FROM #usersurgeons
WHERE isprimary = 0
)usersurgeons
ON usersurgeons.surgeon_id = srp.surgeon_id
LEFT OUTER JOIN
(
SELECT *
FROM surgeons
WHERE isprimary = 0
)LOsurgeons
ON usersurgeons.surgeon_id = LOsurgeons.id
LEFT OUTER JOIN
(
SELECT *
FROM provsurgeons
WHERE isprimary = 0
)LOprovsurgeons
ON LOprovsurgeons.id = LOsurgeons.provsurgeon_id
INNER JOIN
(
SELECT *
FROM #selectedsurgeons
WHERE isprimary = 0
)up
ON up.surgeon_id = rpsur.id
LEFT OUTER JOIN
(
SELECT *
FROM provsurgeons
WHERE isprimary = 0
) ps
ON ps.id = rpsur.provsurgeon_id
WHERE rpsur.isprimary = 0
We are incorporating entity framework in my application. I have converted lot of simple stored procedures into linq queries but this one is giving me trouble and I am not sure where I am going wrong. My brain has turned into jelly and I cannot make it work. I will really appreciate your help so please bear with me.
My SQL is:
SELECT ...
FROM tblProjectAssignment
INNER JOIN tblProjectAssignmentCollection
ON tblProjectAssignment.AssignmentID = tblProjectAssignmentCollection.AssignmentID
RIGHT OUTER JOIN tblCEQRPhaseIIUsers
ON tblProjectAssignment.UserID = tblCEQRPhaseIIUsers.UserID
LEFT OUTER JOIN tblCEQRAccessUserRole
ON tblCEQRPhaseIIUsers.UserRoleTypeID = tblCEQRAccessUserRole.UserRoleTypeID
I need help converting the above to a linq query. I know DefaultIfEmpty() is used for outer joins, but the results that I get when I run the query are incorrect. So, what I need is tblCEQRPhaseIIUsers to get all results independent of the corresponding results in other table. The below query is behaving as all tables has inner joins.
var query = from PATable in db.tblProjectAssignments
from PACTable in db.tblProjectAssignmentCollections.Where(la => la.AssignmentID == PATable.AssignmentID)
from UserTable in db.tblCEQRPhaseIIUsers.Where(la => la.UserID == PATable.UserID).DefaultIfEmpty()
from UserRoleTable in db.tblCEQRAccessUserRoles.Where(la => la.UserRoleTypeID == UserTable.UserRoleTypeID).DefaultIfEmpty()
where (UserTable.FirstName.ToLower().Contains(search.FirstName.Trim().ToLower()) || search.FirstName == null)
where (UserTable.LastName.ToLower().Contains(search.LastName.Trim().ToLower()) || search.LastName == null)
where (PACTable.CEQRNumber.ToLower().Contains(search.CEQRNumber.Trim().ToLower()) || search.CEQRNumber == null)
where (PACTable.ProjectName.ToLower().Contains(search.ProjectName.Trim().ToLower()) || search.ProjectName == null)
where (PATable.IsActive == true)
where (UserTable.IsActive == true)
select new
{
AssignmentID = PATable.AssignmentID,
UserID = PATable.UserID,
FirstName = UserTable.FirstName,
MiddleName = UserTable.MiddleName ?? string.Empty,
LastName = UserTable.LastName,
UserRole = UserRoleTable.UserRoleName,
CEQRNumber = PACTable.CEQRNumber,
ProjectName = PACTable.ProjectName,
CollectionID = PACTable.CollectionID,
EmailAddress = UserTable.EmailAddress,
AssignmentIsActive = PACTable.IsActive
};
The problem is you can't do right joins in linq. The good news is you can rewrite your query to avoid using the right join. You do this by rearranging the tables and using left joins instead.
Currently you are doing this:
select ...
from [MainTable] m
inner join [InnerTable] i on m.Num = i.Num
right join [RightTable] r on m.Num = r.Num
left join [LeftTable] l on r.Num = l.Num
That should be equivalent to this:
select...
from [RightTable] r
left join [LeftTable] l on r.Num = l.Num
left join [MainTable] m on m.Num = r.Num
left join [InnerTable] i on m.Num = i.Num
Now you have a query without right joins, you should be able to write the linq query
I do the fallowing HQL with NHibernate:
from Contact a where IsInternal = 0
this give me the fallowing sql (from NHProfiler):
select TOP ( 25 /* #p0 */ ) contact0_.Id as Id29_,
contact0_.ObjectVersion as ObjectVe2_29_,
...
...
from Contact contact0_
left outer join Company contact0_1_
on contact0_.Id = contact0_1_.Id
left outer join Person contact0_2_
on contact0_.Id = contact0_2_.Id
left outer join Branch contact0_3_
on contact0_.Id = contact0_3_.Id
left outer join ContactGroup contact0_4_
on contact0_.Id = contact0_4_.Id
where contact0_.IsInternal = 0
I want now extend the where-condition with
... and (contact0_1.Id is not null or contact0_2_.Id is not null)
The question is now, how can I access the joined table in the HQL?
Best Regards, Thomas
Er, if the field is null, then referenced object is null. So:
where IsInternal = 0 and (a.person is not null or a.company is not null)
I have the following criteria.
session.CreateCriteria<DomainModels.Models.FreieAbrechnung>()
.CreateAlias("FreieAbrechnungPositionen", "fp", JoinType.LeftOuterJoin)
.Add(Restrictions.Eq("fp.IstAktiv", true))
.Add(Restrictions.Eq("Kunde.ID", kundenId))
.Add(Restrictions.Eq("IstAktiv", true))
.Add(Restrictions.Eq("Abgeschlossen", false))
.SetResultTransformer(CriteriaSpecification.DistinctRootEntity)
.List<DomainModels.Models.FreieAbrechnung>();
So I get this Sql-Statement:
SELECT this_.ID as ID13_1_, this_.Version as Version13_1_,
this_.KundeID as KundeID13_1_, this_.Erstellungsdatum as Erstellu4_13_1_,
this_.Druckdatum as Druckdatum13_1_, this_.Abgeschlossen as Abgeschl6_13_1_,
this_.AbgeschlossenDatum as Abgeschl7_13_1_, this_.IstAktiv as IstAktiv13_1_,
this_.ErstelltDurchID as Erstellt9_13_1_, this_.ErstelltAm as ErstelltAm13_1_,
this_.MutationDurch as Mutatio11_13_1_, this_.MutationAm as MutationAm13_1_,
fp1_.FreieAbrechnungID as FreieAbr3_3_, fp1_.ID as ID3_, fp1_.ID as ID12_0_,
fp1_.Version as Version12_0_, fp1_.FreieAbrechnungID as FreieAbr3_12_0_,
fp1_.KundeID as KundeID12_0_, fp1_.Bezeichnung as Bezeichn5_12_0_,
fp1_.Betrag as Betrag12_0_, fp1_.Erstellungsdatum as Erstellu7_12_0_,
fp1_.MwStSteuerCodeID as MwStSteu8_12_0_, fp1_.IstAktiv as IstAktiv12_0_,
fp1_.ErstelltDurchID as Erstell10_12_0_, fp1_.ErstelltAm as ErstelltAm12_0_,
fp1_.MutationDurch as Mutatio12_12_0_, fp1_.MutationAm as MutationAm12_0_
FROM
FreieAbrechnung this_
left outer join FreieAbrechnungPosition fp1_ on this_.ID=fp1_.FreieAbrechnungID
WHERE
fp1_.IstAktiv = 1 and this_.KundeID = 1 and this_.IstAktiv = 1 and this_.Abgeschlossen = 0
The problem is, that the restrictions fp1_.IstAktiv = 1 is in the where clause. This restrictions have to be in the left outer join. Like this:
left outer join FreieAbrechnungPosition fp1_ on this_.ID=fp1_.FreieAbrechnungID and fp1_.IstAktiv = 1
What should I change in the criteria, that I get the right Sql-Statement?
Thanks,
Dani
To add conditions to the join, you must use HQL instead of criteria.
A rough translation of your query would be:
select f
from FreieAbrechnung f
left join f.FreieAbrechnungPositionen fp with IstAktiv = 1
where f.IsAktiv = 1
... etc