Structuring two conditional left outer joins into one query using LINQ, C# - 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.

Related

Converting SQL to LINQ using MAX

I have two tables
1) T_EJV_CREDIT_DS_INDEX
2) T_EJV_CREDIT_DS_INDEX_CONTRACT
I would like a SQL query like below as a LINQ expression
SELECT MAX(INDEX_FAMILY_VERSION) FROM T_EJV_CREDIT_DS_INDEX cdi
INNER JOIN T_EJV_CREDIT_DS_INDEX_CONTRACT cdic
ON cdic.INDEX_ID = cdi.INDEX_ID
WHERE cdi.INDEX_SHORT_NAME LIKE '%#VARIABLE1%'
AND cdic.TENOR = #VARIABLE2
This is what I have attempted so far
var maxFamilyVersion = (from ic in dsIndexContract
join i in dsIndex on i.INDEX_ID equals ic.INDEX_ID
where i.INDEX_SHORT_NAME.CONTAINS(strindex) && ic.TENOR equals d.TERM
select new
{
ic.INDEX_FAMILY_VERSION.Max()
}).Take(1).ToList();
But the above mentioned starts showing compile issues with the syantax as shown below
Checking for equality in your where condition can be done with ==. The keyword equals is only used in a join condition.
var result = (from ic in dsIndexContract
join i in dsIndex on i.INDEX_ID equals ic.INDEX_ID
where i.INDEX_SHORT_NAME.CONTAINS(strindex) && ic.TENOR == d.TERM
select new
{
ic.INDEX_FAMILY_VERSION.Max()
}).FirstOrDefault();
And instead of .Take(1).ToList(), you can use .FirstOrDefault() to retrieve the first item.
Or a more efficient way is to use .Max() directly instead of .FirstOrDefault():
var result = (from ic in dsIndexContract
join i in dsIndex on i.INDEX_ID equals ic.INDEX_ID
where i.INDEX_SHORT_NAME.CONTAINS(strindex) && ic.TENOR == d.TERM
select ic.INDEX_FAMILY_VERSION).Max();
This should do it:
var maxFamilyVersion =
(from ic in dsIndexContract
join i in dsIndex on ic.INDEX_ID equals i.INDEX_ID
where i.INDEX_SHORT_NAME.CONTAINS(strindex) && ic.TENOR == d.TERM
select ic.INDEX_FAMILY_VERSION).Max();

LINQ Join Tables from Different Database or DataContext

var possibleTPMs = (from ui in db1.Users
from org in db2.Orgs.Where(o => o.OrgId == ui.OrgId && !o.DeletedFlag).DefaultIfEmpty()
where !ui.DeletedFlag && ui.ActiveFlag && ui.OrgId == 1 && ui.UserId != 1
select new { ui.UserId, ui.LastName, ui.FirstName }).ToList();
Above is sample LINQ but having errors, How can I join these two tables that came from two different databases.
To join two 'tables' you simply join them with the below syntax. You shouldn't need any complex join using wheres
var possibleTPMs = (from ui in db1.Users
join org in db2.Orgs on ui.id equals org.id
select new { ui.UserId, ui.LastName, ui.FirstName }).ToList();

How can I with LiNQ select items from a list based on another list?

I am creating a game, and my table layout looks like this:
dbPlayer:
Id (int)
...
dbGame:
Id (int)
Finished (bool)
...
dbGamePlayer:
GameId
PlayerId
...
Given a Players ID, how can I select all games that the player is involved in, but has not (true) finished?
This is what I've go so far:
from g in dbGame
join gp in dbGamePlayer on gp.GameId equals g.Id
join p in dbPlayer on p.Id equals gp.PlayerId
where p.Id == 1 && g.Finished == false
select g
But I'm getting errors all over the place. Sorry, I'm new at LINQ
Your LINQ Statement is wrong.
The joint object (gp) has to be on the right side of the equals-Statement.
join gp in dbGamePlayer on g.Id equals gp.GameId
Same applies to the second join:
join p in dbPlayer on gp.PlayerId equals p.Id
So the full statement should look like:
IEnumerable query = (from g in dbGame
join gp in dbGamePlayer on g.Id equals gp.GameId
join p in dbPlayer on gp.PlayerId equals p.Id
where p.Id == 1 && g.Finished == false
select g);
But the error
Name model is not in scope on the left side of equals.
Consider swapping the expression on the either side of equals.
should have told you that.

Outer join query using LINQ to Entities [duplicate]

This question already has answers here:
Linq-to-Entities Join vs GroupJoin
(3 answers)
Closed 1 year ago.
There are 0 to n departments in my company, 0 to n offices in 1 department, and 0 to n emplyees in 1 office.Now I need a query using linq to list emplyees's average age by department, if nobody in a department then default average is 0.
code is below:
DataContext ctx = new DataContext();
var q0 = from d in ctx.Departments
join o in ctx.Offices on d.Id equals o.DepartmentId
join e in ctx.Employees on o.Id equals e.OfficeId
group e by d into de
select new {
DepartmentId = de.Key.Id,
AverageAge = de.Count() == 0 ? 0 : de.Average(e => e.Age),
};
var q1 = from d in ctx.Departments
join de in q0 on d.Id equals de.DepartmentId into des
from de in des.DefaultIfEmpty()
select new
{
DepartmentName = d.Name,
AverageAge = de == null ? 0 : de.AverageAge
};
var result = q1.ToList();
foreach (var item in result)
{
Console.WriteLine("{0}-{1}", item.DepartmentName, item.AverageAge);
}
ctx.Dispose();
But how to combine q0 and q1 to one query?
Were you meaning something along the lines of:
var newQ2 = from d in ctx.Departments
outer left join o in ctx.Offices on d.Id equals o.DepartmentId
outer left join e in ctx.Employees on o.Id equals e.OfficeId
group e by d into de
select new {
DepartmentId = de.Key.Id,
AverageAge = de.Count() == 0 ? 0 : de.Average(e => e.Age),
};
Changed to:
var newQ2 = from d in ctx.Departments
join o in ctx.Offices on d.Id equals o.DepartmentId
join e in ctx.Employees on o.Id equals e.OfficeId
group e by d into de.DefaultIfEmpty()
select new {
DepartmentId = de.Key.Id,
DepartdentName = select d.Name from d where d.id = de.Key.Id,
AverageAge = de.Count() == 0 ? 0 : de.Average(e => e.Age),
};
Addendum: I would use a sub-select to match up the extra name, not knowing your db layout I have improvised from your code, but you could make it more efficient and have a multipart join based on sub-selects as well. Sorry I cant test this code out at work, I can approximate fairly well, but would need some more info on where your department names are located if you need a more detailed answer:) I have changed the outer left joins back to joins, sorry I forgot in C# with linq you can use DefaultIfEmpty() to cause outer left join behaviour in code.
An outer left join will return nulls where there are no corresponding values, but will allow returns on any parts that do have a corresponding value. Join however will not return any null entries which I suspect is why you had the two queries?
The only caveat on the query I have presented is that you will need to infill any values you require before you use them if they are nulls, for example DepartmentId will need some logic to populate it in case DE is null.
Thank you all,I have got the answer:
var q1 =
from d in ctx.Departments
from o in ctx.Offices.Where(o => o.DepartmentId == d.Id).DefaultIfEmpty()
from e in ctx.Employees.Where(e => e.OfficeId == o.Id).DefaultIfEmpty()
group e by d into de
select new {
DepartmentName = de.Key.Name,
AverageAge = de.Average(e => e == null ? 0 : e.Age),
};

LINQ join with OR

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.

Categories

Resources