Linq outer join using inequality? - c#

In SQL I'd say:
select a.*
from TableA a
left join TableB b on a.Type = b.Type and a.SomeDate < b.AnotherDate
where b.ID is null
This would select all records in TableA where no record exists in TableB of the same Type and later date.
In Linq, how do you do this?
from a in TableA
join b in TableB on a.Type equals b.Type into j // what about the comparator?
from x in j.DefaultIfEmpty()
where x == null
select a;
Thanks!
EDIT:
A few good answers have been proposed, all of which address the specific need expressed in this question, but they're all basically workarounds. They all translate to a nested "exists" queries in one way or another, whereas the SQL in the question is one neat query without any nesting. The case given here is just an example of a general principle; what I'd really like to see is a Linq expression that will translate to (roughly) the syntax of the above SQL query.

Something like this ought to help:
var results =
(from itemA in TableA
from itemB in TableB
where itemA.Type != itemB.Type && itemA.Date < itemB.Date
select itemA).Distinct();

from a in tableA
let rights =
from b in tableB
where a.Type == b.Type && a.Date < b.Date
select b
where !rights.Any()
select a;
It's translated into:
SELECT [t0].[Type] AS [Type], [t0].[SomeDate] AS [SomeDate]
FROM [TableA] AS [t0]
WHERE NOT (EXISTS(
SELECT NULL AS [EMPTY]
FROM [TableB] AS [t1]
WHERE ([t0].[Type] = [t1].[Type]) AND ([t0].[SomeDate] < [t1].[AnotherDate])))

var results = TableA.Where(a =>
!TableB.Any(b => a.Type == b.Type && a.Date < b.Date))
If you want the linq query to be exactly as your SQL you can write:
var result = from a in TableA
from b in TableB.Where(b => a.Type = b.Type && a.SomeDate < b.AnotherDate).DefaultIfEmpty()
where b == null
select a;
But I would say that the first solution is better as the where b == null would result in a filter operation in the queryplan.

Related

How to use LINQ query using OR condition in join clause [duplicate]

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.

Convert SQL Left Join to Linq Expression Left Join

I have a query from SQL and I'm doing a left join. It's working ok.
SELECT Table1.Id, Table1.Comments, Table1.IdListDef
FROM Table2 INNER JOIN Table1 ON Table2.Id = Table1.IdListDef
LEFT JOIN Table3
ON Table1.Id = Table3.IdComments
WHERE Table1.IdListDef = 36 and Table2.IdRev = 1075 and Table3.IdAutor IS NULL
I need to transfrom this query to Linq Expression from C#. How can I do it? I don't know how to transform this query that contains left join.
It should look something like this:
var result = (
from item1 in table1
join item2 in table2 on item1.IdListDef equals item2.Id
join item3 in table3 on item1.Id equals item3.IdComments into finalGroup
from finalItem in finalGroup.DefaultIfEmpty()
where item1.IdListDef == 36 && item2.IdRev == 1075 && finalItem.IdAutor == null
select new
{
item1.Id, item1.Comments, item1.IdListDef
}).ToList();
Addition to your comment, if you're willing to see if any item has id of your outer parameter you could use linq Any extension method:
bool idExists = result.Any(item => item.id == idAutor);
TRY THIS QUERY
VAR OBJLIST=(FROM A IN CONTEX.TABLE2
FROM B IN CONTEX.TABLE1.WHERE(X=>X.IdListDef==a.Id && B.IdListDef==36 && B.IdRev==1075 )
FROM C IN CONTEX.TABLE3.WHRE(X=>X.IdComments==a.Id).DefaultEmpty()
where C.IdAutor==NULL
select new{a.Id, a.Comments, a.IdListDef }).ToList()

Converting SQL Query with Multiple Left Outer Joins to LINQ Query

Struggling with converting a normal SQL Query to a LINQ Query that involves multiple LEFT OUTER JOINS..
Original SQL Query:
SELECT a.*
FROM Testers t
LEFT OUTER JOIN Users u ON u.TesterId = t.TesterId
LEFT OUTER JOIN ValidForms v ON v.DepartmentId = u.DepartmentId
LINQ Query Code is as below:
var x = (from t in Context.Testers.AsEnumerable()
from u in Context.Users
.Where(a => a.TesterId == t.TesterId)
.DefaultIfEmpty()
from v in Context.ValidForms
.Where(b => b.DepartmentId == u.DepartmentId)
.DefaultIfEmpty()
Select new myEntity
{
col1 = t.col1,
col2 = t.col2
}).AsEnumerable()
return x.ToList();
Running the query, I am getting an error: Non-static method requires a target
Appreciate if someone could point out how to do the query properly in LINQ.
I also checked the SO question posted here, but I am unable to grasp the concept provided: SQL to Linq query with multiple left outer joins
Thanks.
Update:
I got this from this SO question.
This is a good way to do it. If you follow that example, your code should look something like this:
var x = (from t in Context.Testers.AsEnumerable()
join u in Context.Users on t.TesterId equals u.TesterId into group1
from a in group1.DefaultIfEmpty()
join v in Context.ValidForms on a.DepartmentId equals v.DepartmentId into group2
from b in group2.DefaultIfEmpty()
select new MyEntity {
col1 = b.col1,
col2 = b.col2
}).AsEnumerable();
UPDATED
var x = (from t in Context.Testers.AsEnumerable()
join u in Context.Users on t.TesterId equals u.TesterId
join v in Context.ValidForms on u.DepartmentId equals v.DepartmentId into group1
from b in group1.DefaultIfEmpty()
select new MyEntity {
col1 = (b != null) ? b.col1 : null,
col2 = (b != null) ? b.col2 : null
}).AsEnumerable();

let statement and outer join in LINQ to SQL

How to use the let statement in outer join of LINQ to SQL?
Something like below,
from a in tableA
from b in tableB.where(o => a.Key == o.Key).DefaultIfEmpty() //outer join
let x = b.objectBB
where b != null && x != null && x.FilterCode == "X"
So,
Does the above query is fine?
Will the 'let' fail if 'b' is null ?
what will happen with the where clause on 'x'? Does this filter work fine?
Any better way to achieve this also much welcome.!
Seems to me that if you want both sides to be not null, don't you just want an inner join?
from a in tableA
join b in tableB on a.Key equals o.Key
where b.FilterCode == "X"

LINQ to Entities Join on Nullable Field where Null Implies "Match All"

I am attempting to run the following LINQ query using Entity Framework 5:
int taskId = 2;
query = from a in Table_A
where a.StatusCode != "DONE"
&& a.Inbound
join b in Table_B
on a.Id equals b.Id_Table_A
join c in Table_C
on a.State equals (c.State ?? a.State)
where 2 == c.Id_Task
&& b.DataType == c.DataType
select a.Id;
The line that is causing me problems is:
on a.State equals (c.State ?? a.State)
The "State" field in Table_C is nullable... and when it is null, it is used to imply "all states". As such, when "c.State" is null I want the record to be matched. If I were to write this in SQL, I would use the following:
JOIN Table_C ON Table_A.State = ISNULL(Table_C.State, Table_A.State)
Unfortunately, I am being given the following error:
The name 'a' is not in scope on the right side of 'equals'. Consider swapping the expressions on either side of 'equals'.
I will be grateful to anybody who can let me in on the secret to getting this working.
Thanks.
You can modify your code like:
int taskId = 2;
query = from a in Table_A
where a.StatusCode != "DONE"
&& a.Inbound
join b in Table_B
on a.Id equals b.Id_Table_A
from c in Table_C
where 2 == c.Id_Task
&& b.DataType == c.DataType
&& (c.State == null || a.State.Equals(c.State))
select a.Id;
A join is essentially a where clause, so here we can use the where clause due to the restrictions with join.
I managed to get this to work by moving the "DataType" check from the WHERE to the JOIN, and moving the "State" check from the JOIN to the WHERE. The resulting code that worked as I expected is as follows:
query = from a in Table_A
where a.StatusCode != "DONE"
&& a.Inbound
join b in Table_B
on a.Id equals b.Id_Table_A
join c in Table_C
on b.DataType equals c.DataType
where 2 == c.Id_Task
&& (c.State ?? a.State) == a.State
select a.Id;
Many thanks to everybody who has taken a look at this for me. :)
You can use "from" syntax instead of "join"
from a in TableA
from b in TableB
.Where(x => (x.Buy ?? a.Buy) == a.Buy
&& (x.Parity ?? a.Parity) == a.Parity)

Categories