LINQ join with OR - c#

I want to do a JOIN with LINQ using an OR statement.
Here is the SQL query I'm starting with:
SELECT t.id
FROM Teams t
INNER JOIN Games g
ON (g.homeTeamId = t.id OR g.awayTeamId = t.id)
AND g.winningTeamId != 0
AND g.year = #year
GROUP BY t.id
I'm having trouble converting that ON clause to LINQ. This is where I'm at:
var y = from t in db.Teams
join g in db.Games on t.ID equals g.AwayTeamID //missing HomeTeamID join
where g.WinningTeamID != 0
&& g.Year == year
group t by t.ID into grouping
select grouping;
I think I could use:
join g in db.Games on 1 equals 1
where (t.ID == g.HomeTeamID || t.ID == g.AwayTeamID)
and this works but seems kind of seems hacky. Is there a better way?

I struggled with this as well until I found the following solution, which worked well for my situation:
var y = from t in db.Teams
from g in db.Games
where
(
t.ID == g.AwayTeamID
|| t.ID == g.HomeTeamID
)
&& g.WinningTeamID != 0
&& g.Year == year
group t by t.ID into grouping
select grouping;
Under the covers, your solution probably works very close to this one. However, I bet this one is just a bit faster if you benchmark it since it is not JOINING every item in the first dataset with every item in the second dataset, which could be a disaster if either (or both) dataset were really big.

The where clause applies a boolean condition, so using "||" is the way to go. You can chain multiple where clauses but I believe that will give you a "and" operation, rather than an "or".

I think you can do like this:
from t1 in db.Table1
// inner join with OR condition
from t2 in db.Table2 where t1.col1 == t2.col1 || t1.col2 == t2.col2
// normal inner join
join t3 in db.Table3 on t1.col1 equals t3.col1
// inner join with complex condition
join t4 in db.Table4 on t2.col4 equals t4.col4 where t2.col5.Contains(t4.col5)
// left join with OR condition
from t5 in db.Table5.Where(x => x.col5 == t1.col5 || x.col6 == t1.col6).DefaultIfEmpty()
select new {
x = 1 // select whatever you want here
}
The underlying SQL query probably won't use native sql joins but the above is just a way to make your code look pretty and organized.

Related

Optional condition in join clause - Linq

I have below linq query
var resultGuardian = from s in _db.Students
join sg in _db.StudentGuardians on s.StudentID equals sg.StudentID
join g in _db.Guardians on sg.GuardianId equals g.GuardianId
join lr in _db.luRelationTypes on sg.RelationTypeID equals lr.RelationTypeID
join ga in _db.GuardianAddresses on g.GuardianId equals ga.GuardianId
join ad in _db.Addresses on ga.AddressID equals ad.AddressID
join lt in _db.luAddressTypes on ad.AddressTypeID equals lt.AddressTypeID
join lg in _db.luGenders on g.GenderID equals (int?)lg.GenderID into ssts
from gdnr in ssts.DefaultIfEmpty()
where
s.TenantID == tenantid && sg.TenantID == tenantid && g.TenantID == tenantid &&
s.StatusId == (int?)Extension.StatusType.Active //1
&& g.StatusID == (int?)Extension.StatusType.Active &&
lr.StatusID == (int?)Extension.StatusType.Active && lr.TenantID == tenantid &&
s.StudentID == sid
select new
{
g.FirstName,
g.LastName,
IsPrimary = sg.IsPrimaryGuardian,
g.Education,
g.Email,
g.Phone,
lr.RelationCD,
ga.IsStudentAddress,
gdnr.GenderCD,
lt.AddressName,
ad.Address1,
ad.Address2,
ad.City,
ad.State,
ad.Zipcode
};
In above query when ad.AddressTypeID is null, it is not returning any result.
I have requirement if ad.AddressTypeID is null,than from LuAddressTypes fetch default record where AddressTypeCd=1. If I try this way
join lt in LuAddressTypes on ad.AddressTypeID equals lt.AddressTypeID into v1
from v2 in v1.DefaultIfEmpty()
select new
{
v2.AddressName,
g.Phone,..
});
in result v2.AddressName always returning null. I am unable to specify AddressTypeCd=1 where condition as well. AddressTypeCd=1 is not ad.AddressTypeID.
I need v2.AddressName where AddressTypeCd=1. How can I do that?
Find related entities all related entities
You can't use the standard LINQ join, but in LINQ to Entities you could use the alternative join syntax based on correlated Where - EF is smart enough to translate it to JOIN.
In your case, instead of
join lt in _db.luAddressTypes on ad.AddressTypeID equals lt.AddressTypeID
you could use
from lt in _db.luAddressTypes.Where(lt => ad.AddressTypeID == lt.AddressTypeID
|| (ad.AddressTypeID == null && lt.AddressTypeCd == 1))
which is translated to something like this
INNER JOIN [dbo].[LuAddressTypes] AS [Extent3]
ON ([Extent2].[AddressTypeID] = [Extent3].[AddressTypeID])
OR (([Extent2].[AddressTypeID] IS NULL) AND (1 = [Extent3].[AddressTypeCd]))
If you want LEFT OUTER JOIN, simply add .DefaultIfEmpty() at the end of the above line.

Structuring two conditional left outer joins into one query using LINQ, C#

Currently, I am executing two queries based upon whether w.Type is either 1 or 2. If w.Type is 1 we perform a join to the Issues table and if the Type is 2 we join to the TSubs table. I am trying to merge these queries into one.
var productIdOne = (from w in listAbandonedCarts
join i in Issues on w.ProductId equals i.Id
where w.Type == 1
select new { i.Title.Name }).ToList();
var productIdTwo = (from w in listAbandonedCarts
join ts in TSubs on w.ProductId equals ts.Id
where w.Type == 2
select new { ts.Title.Name }).ToList();
I am considering using two left outer joins based upon this SQL psuedo-code
SELECT*
FROM P_carts pc
LEFT OUTER tSubs ts on ts.id = pc.productid and pc.Type = 2
LEFT OUTER issues i on i.id = pc.productid and pc.Type = 1
So far i have some linq pseudo coded but i'm struggling to get the syntax correct for the two conditional joins
var listProducts = (from w in listAbandonedCarts
join i in Issues on w.ProductId equals i.Id into iN && w.ProductId == 1
from i in iN.DefaultIfEmpty()
join into ts in TSubs.....
The problem I'm struggling with is that this isn't a double left outer its two separate joins. My current error is that i cannot have w.ProductId after the equals because I'm out of scope of w at this point and can't figure out how to structure the linq statement. Any help would be much appreciated, thanks!
Give this a shot:
var productIds = (
from w in listAbandonedCarts
from i in Issues.Where(issue => issue.Id == w.ProductId && w.Type == 1).DefaultIfEmpty()
from t in TSubs.Where(tsub => tsub.Id == w.ProductId && w.Type == 2).DefaultIfEmpty()
select new {Name = (w.Type == 1 ? i.Title.Name : t.Title.Name)})
.ToList();
var listProducts = (from w in listAbandonedCarts
join i in Issues on w.ProductId equals i.Id && w.ProductId == 1 into iN
from i in iN.DefaultIfEmpty()
join into ts in TSubs on ts.id equals pc.productid into tsBag
from ts in tsBag.DefaultIfEmpty()
select new
{
// All columns you need
};
Please try the above linq. This sample code is not compiled nor tested. Please use this for reference purpose only.

Checking a null value in a join query after 2 joins and a where or condition

I have a linq query that is checking to see if a value exists, given criteria. It joins Permits, AccessControlTypes, and EntanceEnhancementHistories, all on their Guid FKs. It checks Permits of Type "Full" or "Limited" to see if they have any associated EntranceEnhancementHistories.
bool LimitedorFullEntranceEnhancementValue = (
from p in context.Permits
join a in context.AccessControlTypes on p.EntranceAccessControlTypeGUID equals a.GUID
join e in context.EntranceEnhancementHistories on p.GUID equals e.PermitGUID
where p.GUID.Equals(PermitGuid)
&& ((a.Description.Equals("Full") || a.Description.Equals("Limited"))
&& (e.GUID == null))
select e).Any();
return LimitedorFullEntranceEnhancementValue;
This query is failing. It's returning false in the case that is does find description of full or limited, but does not find an entrancehancementvalue (it should be null).
It works properly without the check for a null entranceenhancementvalue, and I'm able to get it working with 2 queries, but I don't think this should be necessary.
You're using an "inner join" on p.GUID equals e.PermitGUID, which means you'll only see results where an e.PermitGUID exists. I think you want something more like this:
(from p in context.Permits
join a in context.AccessControlTypes on p.EntranceAccessControlTypeGUID equals a.GUID
where p.GUID.Equals(PermitGuid)
&& ((a.Description.Equals("Full") || a.Description.Equals("Limited"))
&& !context.EntranceEnhancementHistories.Any(e => p.GUID == e.PermitGUID)
select p).Any();
You need to use a LEFT OUTER JOIN if you expect to see rows where the Guid key is null:
(from p in context.Permits
join a in context.AccessControlTypes on p.EntranceAccessControlTypeGUID equals a.GUID
join t in context.EntranceEnhancementHistories on p.GUID equals e.PermitGUID into leftJoin
join e in leftJoin on p.DefaultIfEmpty()
where p.GUID.Equals(PermitGuid)
&& ((a.Description.Equals("Full") || a.Description.Equals("Limited"))
&& (e.GUID == null))
select e).Any();
return LimitedorFullEntranceEnhancementValue;

C# SQL/Linq/Entity Framework calculating column totals for multiple columns from large data source

Sorry to bother you, however I'm having issues converting my SQL Query into C# Entity Framework.
My SQL query is as follows:
SELECT CAST(ROUND(sum(size/rate), 0) AS INT) s,
CAST(ROUND(sum(PL/rate), 0) AS INT) PL
FROM [bs].[b] b
join [bs].[s] s on b.id = s.b_id
join [bs].[o] o on s.o_id = o.id
join [bs].[a] a on o.a_id = a.id
join [fs].[f] f on b.f_id = f.id
where f.r_date
between '2013-05-01 00:00:00.000'
and '2013-05-31 00:00:00.000'
and s.deleted_at is NULL
and b.group_id = '0'
and (o.a_id = 50 or o.a_id = 52)
I have in turn managed to get all the joins done and where statement in place (a.k.a. 'The Easy Bit') however I just cannot find a way to get those sums for the column totals to work.
This is what I have in place so far:
var GroupSk = (from Bs in sb.b
join S in sb.s on Bs.id equals S.b_id
join O in sb.o on S.o_id equals O.id
join A in sb.a on O.a_id equals A.id
join Fs in sb.vw_f on Bs.f_id equals Fs.f_id
where Fs.r_date >= t_FromDate && Fs.r_date <= t_ToDate
where S.deleted_at == null
where Bs.group_id == 0
where O.a_id == 50 || O.a_id == 52
select new {
As you can see, it's everything up until the SUM part of the query.
This query can return anywhere from 1-150000 rows, and I need a way to ensure that the column totals I get back are returned in a timely manner.
I had originally planned on using a ForEach loop but had trouble implementing it (along with the fact that it'll probably take a LONG time if a larger number of rows are returned).
I'm aware there are a few 'sum column total' questions out there, however they don't deal with multiple tables and multiple column outputs. They also appear to be limited to 2 or 3 columns total, whereas my tables far exceed that.
Any & all help would be greatly appreciated.
It's a bit of a hack, but it works. The trick is to make one group containing all items and then do the sums over the group:
var GroupSk = (from Bs in sb.b
join S in sb.s on Bs.id equals S.b_id
join O in sb.o on S.o_id equals O.id
join A in sb.a on O.a_id equals A.id
join Fs in sb.vw_f on Bs.f_id equals Fs.f_id
where Fs.r_date >= t_FromDate && Fs.r_date <= t_ToDate
where S.deleted_at == null
where Bs.group_id == 0
where O.a_id == 50 || O.a_id == 52
select new { r1 = ??.size / ??.rate, r2 = ??.PL / ??.rate })
.GroupBy(x => 0)
.Select(g => new {
R1 = g.Sum(x => x.r1),
R2 = g.Sum(x => x.r2)
});
I put ?? marks where I didn't know the origin of the properties, so you'll have to substitute the right variable names there. (Bs, S, O, A, Fs).
This will translate into one SQL query, so all the processing is done by the database engine and only the small result object is transferred over the wire.

linq join 3 tables with or condition

I need to create a statement in LINQ with 3 tables and OR condition.
My function receives an integer, lets call it intZ. I have 3 tables: tableA, tableB and tableC.
tableA has columns int1, int2 and intB. intB is related to tableB.
problem: int1 or int2 of tableA can be intZ and it has to match with one tableC record.
I need an OR condition, but I have no idea where to place it. Does it go in the where clause? Or in the equals clause?
At the moment, I know how to join 3 tables, but the condition is killing me.
What is the difference between the two ways to create statements in linq? Is there a performance impact?
edit: Okay, now I think it's more clear. intZ has to be related with intC from tableC, and this number can be int1 or int2 of tableA.
Just add it to a Where. In Linq2Sql this will be translated to an inner join (with or) on tableB
from a in tableA
from b in tableB.Where(x => x.A == a.A || x.B == a.B)
select new { a, b };
You can't use an "or" condition in joins in LINQ, as it only supports equijoins. But you should be able to do it in a where clause with no problems. For example:
var query = from rowC in tableC
where rowC.intC == intZ
from rowA in tableA
where rowA.int1 == rowC.intC || rowA.int2 == rowC.intC
join rowB in tableB on rowA.intB equals rowB.intB
select new { rowA, rowB, rowC };
This may be helpful.
var locations = from r1 in
(from a in context.A
join b in context.B
on a.ID equals b.ID
select new
{
a.Prop1,
a.Prop2,
b.Prop3,
b.ID
})
join c in context.C
on r1.ID equals c.ID
select new
{
r1.Prop1,
r2.Prop2,
r2.Prop3,
c.Prop4
};
For the life of me, I couldn't get the .Where to work in my query (perhaps it's how I'm using LinqPad) but I was able to get the following to work:
from s in Stores
join a in Areas on s.AreaID equals a.ROWID
join r in Regions on a.RegionID equals r.ROWID
join e in Employees on 1 equals 1 // <-- produces a cartesian product
join t in Titles on e.TitleID equals t.ROWID
where e.AreaID == a.ROWID || e.RegionID == r.ROWID // <--filters the data based on OR stmt
where s.StoreNum == 469
select new { r.RegionName, a.AreaName, s.StoreNum, s.StoreName, t.JobCode, e.FirstName, e.LastName }
Try this:-
var result= tableA.SelectMany(a => tableB.Where(x => x.A == a.A || x.B == a.B), (a, b) => new {a, b});

Categories

Resources