Inner Join with two equalities inside on clause in LINQ Lambda - linq

I'm trying to convert a Sql query to a Linq Lambda style query. Thought this would be something easy but it turned out not.
SQL Query is as follows;
select distinct t1.ID from table1 t1
inner Join table2 t2on (t2.FromId= t1.Id or t2.ToId= t1.Id)
where t1.TenantId = 12
and t2.wId= 51
All examples I came across are for one clause joins so far. I wrote something like this
actStaList = _db.t1
.Join(_db.t2,
s => s.ID,
wf => wf.ToId,
(s, wf) => new { t1= s, t2= wf }
)
.Where(a => a.t1.Tenant.Guid == _tenantGuid)
.Select (m=>m.t1.ID)
.ToList();
It is obvious this won't work as the sql query above but still it's a start.
Still I can't figure where should I add the second part inside INNER JOIN and Distinct keyword.

One option you have is to use two separate Linq Queries and concat the result(and eliminating duplicates).
var left = t1.Join(t2,
s => s.ID,
wf => wf.ToId,
(s, wf) => new { t1= s, t2= wf }
).Select(x=>x);
var right = t1.Join(t2,
s => s.ID,
wf => wf.FromId,
(s, wf) => new { t1= s, t2= wf }
).Select(x=>x);
var actStaList = left.Concat(right).Select(m=>m.t1.ID)
.Distinct();
Please note I have omitted the Where Clause in the example as in the OP, both Sql version and your attempted Linq version seem to have different conditions. You can add them yourself.

The LINQ Join statement only supports equi-joins. For other types of equality you can't use the Join statement and have to code the equality manually. This is much easier in query syntax:
actStaList = (
from t1 in _db.table1
from t2 in _db.table2
where t2.FromId == t1.Id || t2.ToId == t1.Id
where t1.TenantId == 12 && t2.wId == 51
select t1.ID
).Distinct();
For the record, you can avoid the Distinct statement by executing this as a SQL EXISTS statement:
actStaList =
from t1 in _db.table1
where t1.TenantId == 12
where (from t2 in _db.table2
where t2.wId == 51 && (t2.FromId == t1.Id || t2.ToId == t1.Id)
select t2).Any()
select t1.ID;

Related

Linq left outer join with multiple condition

I am new to Linq. I am trying to query some data in MS SQL.
Here is my statement:
select * from booking
left outer join carpark
on booking.bookingId = carpark.bookingId
where userID = 5 and status = 'CL'
When I run this in MS SQL, I get the expected result. How can I do this in Linq?
Thank you for your help.
you need this:
var query = (from t1 in tb1
join t2 in tb2 on t1.pKey = t2.tb1pKey into JoinedList
from t2 in JoinedList.DefaultIfEmpty()
where t1.userID == 5 && t1.status == "CL"
select new
{
t1,
t2
})
.ToList();
You can try to do left join this way :
from t1 in tb1
from t2 in tb2.Where(o => o.tb1pKey == t1.pKey).DefaultIfEmpty()
where tb1.userId == 5 && tb1.status == "CL"
select t1;
Usually when people say they want a "left outer join," that's just because they've already converted what they really want into SQL in their head. Usually what they really want is all of the items from table A, and the ability to get the related items from table B if there are any.
Assuming you have your navigation properties set up correctly, this could be as easy as:
var tb1sWithTb2s = context.tb1
.Include(t => t.tb2s) // Include all the tb2 items for each of these.
.Where(t => t.userID == 5 and t.status = "CL");

Linq 2 SQL left Join with mono

I cant get the fallowing request working in Mono:
Without joins it would work.
when I only select t1 it also works, but I cant select something from both tables.
I think I want a left join, where I always have entries in t1 and IF the NameOfFile matches FileName then I want to have the tables joined.
Extra Question: When is my query executed? When I run the foreach loop?
var result = (
from t1 in db.Table1
join t2 in db.Table2 on t1.FileName equals t2.NameOfFile
into joinDep
from t3 in joinDep.DefaultIfEmpty ()
select new
{
Time = t1.WriteTime,
Name = t2.NameOfFile
}
)
.OrderByDescending (c => c.Time.Date)
.Take (10);
foreach (var entry in result)
{
Console.WriteLine (entry.Name );
}
Use this:
var query = from t1 in db.Table1
join t2 in db.Table2 on t1.FileName equals t2.NameOfFile into gj
from joinDep in gj.DefaultIfEmpty ()
select new
{
Time = t1.WriteTime,
Name = joinDep.NameOfFile
};
var result = query.OrderByDescending (c => c.Time.Date)
.Take (10);
Yes. Take uses deferred execution.

How to write this LINQ Query in a better way

I have one Linq Query. When I run the query, Only for 10 records its taking 13 seconds to extract the data to the model. I need to know the query which I wrote is good for performance or not. Please guide me what i am doing wrong.
Code
var stocktakelist = (from a in Db.Stocktakes
select new ExportStock
{
Id = a.Id,
ItemNo = a.ItemNo,
AdminId = (from admin in Db.AdminAccounts where admin.Id == a.Id select admin.Name).FirstOrDefault(),
CreatedOn = a.CreatedOn,
Status = (from items in Db.Items where items.ItemNo == a.ItemNo select items.ItemStatu.Description).FirstOrDefault(),
Title = (from tit in Db.BibContents where tit.BibId == (from bibs in Db.Items where bibs.ItemNo == a.ItemNo select bibs.BibId).FirstOrDefault() && tit.TagNo == "245" && tit.Sfld == "a" select tit.Value).FirstOrDefault() // This line of Query only makes the performance Issue
}
).ToList();
Thanks
The reason this is so slow is because it is running the 3 inner LINQ statements for every item in the outer LINQ statement.
Using LINQ joins will run only 4 queries and then link them together, which is faster.
To find out how to join, there are plenty of resources on the Internet depending on the type of LINQ you are using.
If you're retrieving this data from a SQL server, perhaps consider doing this intensive work in SQL - this is what SQL was designed for and it's much quicker than .NET. EDIT: As highlighted below, the work is done in SQL if using LINQ to SQL/Entities and using the correct join syntax.
I was trying to create the corresponding query with some joins for practice.
I cannot test it and i'm not 100% sure that this query will you get the result
you are hoping for but maybe at least it will give you a hint on how to write
joins with linq.
from a in Db.Stocktakes
join admin in Db.AdminAccounts
on a.Id equals admin.Id
into adminJoinData
from adminJoinRecord in adminJoinData.DefaultIfEmpty( )
join items in Db.Items
on a.ItemNo equals items.ItemNo
into itemsJoinData
from itemsJoinRecord in itemsJoinData.DefaultIfEmpty( )
join title in Db.BibContents
(
from subQuery in Db.BibContents
where subQuery.TagNo == "245"
where subQuery.Sfld == "a"
select subquery
)
on title.BibId equals itemsJoinRecord.BidId
into titleJoinData
from titleJoinRecord in titleJoinData.DefaultIfEmpty( )
select new ExportStock( )
{
Id = a.Id,
ItemNo = a.ItemNo,
AdminId = adminJoinRecord.Name,
CreatedOn = a.CreatedOn,
Status = itemsJoinRecord.ImemStatu.Description,
Title = titleJoinRecord.Value
}
As others have said, you should use Left Outer Joins in your LINQ just as you would if writing it in SQL.
Your query above will end up looking roughly like this once converted (this is untested, but gives the basic idea):
var a = from a in Db.Stocktakes
join admin in Db.AdminAccounts on admin.Id equals a.Id into tmpAdmin
from ad in tmpAdmin.DefaultIfEmpty()
join item in Db.Items on item.ItemNo equals a.ItemNo into tmpItem
from it in tmpItem.DefaultIfEmpty()
join title in Db.BibContents on bib.BibId equals items.BibId into tmpTitle
from ti in tmpTitle.DefaultIfEmpty()
where ti.TagNo == "245"
&& ti.Sfld == "a"
select new ExportStock
{
Id = a.Id,
ItemNo = a.ItemNo,
AdminId = ad == null ? default(int?) : ad.Id,
CreatedOn = a.CreatedOn,
Status = it == null ? default(string) : it.ItemStatus.Description,
Title = ti == null ? default(string) : ti.Value
};
Using lambda expressions your query will look like this:
Db.Stocktakes
.Join(Db.AdminAccounts, a => a.Id, b => b.Id, (a,b) => new { a, AdminId = b.Name })
.Join(Db.Items, a => a.ItemNo, b => b.ItemNo, (a,b) => new { a, Status = b.ItemStatus.Description, BidId = b.BibId })
.Join(Db.BibContents, a => a.BibId, b => b.BibId, (a,b) => new { a, Value = b.Value, TagNo = b.TagNo, Sfld = b.Sfld })
.Where(a => a.TagNo == "245" && a.Sfld == "a")
.Select(a =>
new ExportStock { Id = a.Id,
ItemNo = a.ItemNo,
AdminId = a.AdminId,
CreatedOn = a.CreatedOn,
Status = a.Status,
Title = a.Value
}
).ToList();

How to do a "join" in a LINQ query against the Entity Framework

I have the following table structure which has been imported into the Entity Framework. I need to write a LINQ query where I select the entities of Table1, where a field in Table2 is equal to true, and a field in Table 3 is equal to a specific GUID.
Could someone help with this?
Thanks you.
alt text http://digitalsamurai.us/images/drawing2.jpg
Try:
from t3 in dataContext.Table3
where t3.Guidfield == someGuid
from t2 in t3.Table2
where t2.Field // boolean field is true
select t2.Table1;
EDIT: As requested, equivalent lambda expression syntax:
dataContext.Table3.Where(t3 => t3.Guidfield == someGuid)
.SelectMany(t3 => t3.Table2)
.Where(t2 => t2.Field)
.Select(t2.Table1);
from t1 in table1
join t2 in table2
on t1.table1PK equals t2.table1PK
join t4 in table4
on t2.table2PK equals t4.table2PK
join t3 in table3
on t3.table3PK equals t4.table3PK
where t2.randomBoolColumn == true && t3.GUID == myGUIDVariable
select t1;

How do I use subquery, groupby, max, and top in single linqToSql statement?

Using LinqToSql, I need to return a single (L) for the most recent modDate in a join table (CL).
Tables:
L (Lid, meta1, meta2, ...)
CL (Cid, Lid, ModDate)
Here is sql that produces the expected result
SELECT l.*
FROM L l
INNER JOIN (
SELECT TOP 1 cl.Lid, MAX(cl.ModDate) as ModDate
FROM CL cl
INNER JOIN L l ON cl.Lid = l.Lid AND l.meta1 = 5
GROUP BY cl.Lid
ORDER BY MAX(cl.ModDate) DESC
) As m ON l.Lid = m.Lid
Simple enough. The subquery projects us to the ids. The query fetches those records with matching ids.
var subquery = db.L
.Where(L => L.meta1 = 5)
.SelectMany(L => L.CLs)
.GroupBy(CL => CL.Lid)
.OrderByDescending(g => g.Max(CL => CL.ModDate))
.Select(g => g.Key)
.Take(1)
var query = db.L
.Where(L => subquery.Any(id => L.Lid == id))
Reflecting on this further, you can get away from the subquery:
var query = db.L
.Where(L => L.meta1 = 5)
.SelectMany(L => L.CLs)
.GroupBy(CL => CL.Lid)
.OrderByDescending(g => g.Max(CL => CL.ModDate))
.Select(g => g.First().L);
As your provided query, I can interpret into this Linq.
var query = from l in Context.L
join m in (from cl in Context.CL
join l in Context.L on cl.Lid equals l.Lid
where l.meta1 == 5
group new { l.Lid, cl.ModDate } by cl.Lid into grp
select new { Lid = grp.Key, ModDate = grp.Max(g => g.ModDate) } into grp
order by grp.ModDate descending
select grp).Take(1) on l.Lid equals m.Lid
select l;
My SQL-fu isn't fabulous and it's before my first coffee, so I assume "l" in the outer query ends up being a completely different "l" to the one in the subquery?
I think this will do it, but you'll have to try to be sure :) It'll be well worth checking what the generated SQL looks like. If you didn't mind it executing as two queries, of course, it would be somewhat simpler.
// Can't do the "Take(1)" here or it will be executed separately
var subquery = from cl in context.CL
join l in context.L on cl.Lid = l.Lid
where l.meta1 = 5 // could put this in join clause
group cl.ModDate by cl.lid into grouped
order by grouped.Max() descending
select grouped.Key;
// But can take the first result of the join
// This may be simpler using dot notation instead of a query expression
var query = (from l in context.L
join lid in subquery
select l).Take(1);
(EDIT: I wasn't taking the max ModDate before. Doh. Also simplified grouping by using the ID as the key (which it was already) so we only need the ModDate as the group values.)

Resources