Multiple Join with Linq to Entity - sql-server

I would like to write the following SQL queries into Linq To Entity queries.
SELECT *
FROM Table1
LEFT JOIN Table2 ON Table2.IdTable1 = Table1.Id
INNER JOIN Table3 ON Table3.IdTable2 = Table2.Id
SELECT *
FROM Table1
LEFT JOIN Table2 ON Table2.IdTable1 = Table1.Id
LEFT JOIN Table3 ON Table3.IdTable2 = Table2.Id
WHERE Table3.Id IS NULL
For the first one, I'm working on this :
var query = RepoTable1.AsQueryable();
[some query = query.Where(...)
query = query
.Join(RepoTable2.AsQueryable(), e => e.IdTable1, i => i.Id, (i, e) => i)
.GroupJoin(RepoTable3.AsQueryable(), c => c.IdTable2, eq => eq.Id, (eq, c) => eq);
There is something I don't understand about the Join.
Actually, "eq => eq.Id" should be of type "Table2", but it's "Table1".
If I change "(i, e) => i" to "(i, e) => e", I get an error of compilation because "query" is waiting "Table1" type.
And for the second query, I don't know how to add the "Where()" to the Join (actually, I'm mostly stuck with the first one)
Thanks in advance! Please tell me if this is not clear enough!
Final answer :
query = (from t1 in query
join t2 in RepoTable2.AsQueryable() on t1.Id equals t2.IdTable1 into joint1t2
from t2 in joint1t2.DefaultIfEmpty() // DefaultIfEmpty is used for left joins
join t3 in RepoTable3.AsQueryable() on t2.Id equals t3.IdT
select t1);
and for the second query :
query = (from t1 in query
join t2 in RepoTable2.AsQueryable() on t1.Id equals t2.IdTable1 into joint1t2
from t2 in joint1t2.DefaultIfEmpty() // DefaultIfEmpty is used for left joins
join t3 in RepoTable3.AsQueryable() on t2.Id equals t3.IdTable2 into joint2t3
from t3 in joint2t3.DefaultIfEmpty() // DefaultIfEmpty is used for left joins
where t3.Id== null
select t1);

I would recommend using query syntax (If you can't use navigation properties)
Your first query would look quite similar using this syntax
var query =
from t1 in RepoTable1.AsQueryable()
join t2 in RepoTable2.AsQueryable() on t1.Id equals t2.Id into t2g
from t2 in t2g.DefaultIfEmpty() //DefaultIfEmpty is used for left joins
join t3 in RepoTable3.AsQueryable() on t2.Id equals t3.Id
select new
{
t1, t2, t3
};

Related

MSSQL - Adding condition on the where and on clause performance

Consider the following two queries:
select *
from
table1 t1
left join
table2 t2
on t1.Id = t2.t1Id and (t1.Status = 1 or t2.Id is not null)
And this one
select *
from
table1 t1
left join
table2 t2
on t1.Id = t2.t1Id
where
t1.Status = 1 or t2.Id is not null
The first one runs in 2 seconds. The second one in 2 minutes. Shouldn't the execution plan be the same?
The query plans are different because the queries (and results) are different.
You're using a LEFT JOIN, so the first query will return rows with NULL values where not in table 2.
The second query will not return those rows.
If it was an INNER JOIN, they would essentially be the same query.
Here the Below Query Returns all the "Table1" results with additional matching Columns based on the "ON Clause" condition.
select * from table1 t1
left join table2 t2
on t1.Id = t2.t1Id and (t1.Status = 1 or t2.Id is not null)
Now, the below query matches the 2 tables and returns the rows based on the ON Clause and an additional WHERE Clause filters the Rows again based on the Condition.
select * from
table1 t1
left join table2 t2 on t1.Id = t2.t1Id
where t1.Status = 1 or t2.Id is not null
Here, Even though we used LEFT JOIN But in this case it acts like an INNER JOIN
So, Here Both the Queries produce Different Result Sets. The Execution Plan Also Vary which results in Different Execution Time.
The best way to deal with an OR is to eliminate it (if possible) or break it into smaller queries. Breaking a short and simple query into a longer, more drawn-out query may not seem elegant, but when dealing with OR problems, it is often the best choice:
select *
from table1 t1
left join table2 t2 t1.Id = t2.t1Id
where t1.Status = 1
union all
select *
from table1 t1
left join table2 t2 t1.Id = t2.t1Id
where t2.Id is not null
You can read more in this article:
https://www.sqlshack.com/query-optimization-techniques-in-sql-server-tips-and-tricks/

Sum on subqueries on EF Core Linq

I want to get a result by typing subquery into the sum query.
The following code works when I write to Sql.
But on EF, how do I add another select at the top?
SQL
This code is work.
select
sum (data.rate)
from
(
SELECT
t1.Id,
(c.rate /
(SELECT COUNT(1) FROM [table4] AS [t4] WHERE ([t4].[FKId] = p1.Id))) as rate
FROM [table1] AS [t1]
INNER JOIN [table2] AS [t2] ON ([t1].[FKId] = [t2].[Id])
INNER JOIN [table3] AS [t3] ON ([t1].[FKId] = [t3].[Id]))
as data
C#
var data = await (
????
from t1 in ctx.table1
join t2 in ctx.table2 on new { t1.FKId} equals new { FKId = t2.Id}
join t3 in ctx.table3 on new { t1.FKId} equals new { FKId = t3.Id}
select new
{
rate = t3.Rate /
(from t4 in ctx.table4
where t4.FKId == t2.Id
select t4.Id)
.Count())
})
.SumAsync(sm => (double?)sm.rate ?? 0);
This c# code is not working.
Error:
Cannot perform an aggregate function on an expression containing an
aggregate or a subquery.
Per this answer, EF Core 3.0 still can't handle this situation (!).
However, you can translate the query without a subquery (effectively, using SelectMany) and it should work:
var ans = await (from t1 in ctx.table1
join t2 in ctx.table2 on t1.FKId equals t2.Id
join t3 in ctx.table3 on t1.FKId equals t3.Id
from t4 in ctx.table4
where t4.FkId == t2.Id
group t4.Id by new { t3.rate, t2.Id } into t2g
select t2g.Key.rate / t2g.Count()
)
.SumAsync();

Different Filters for Different Columns

I am relatively new to SQL and I have the following question. I have the following code:
Select * from table1
LEFT JOIN table2 ON table1.name = table2.name and table1.id = table2.id
LEFT JOIN (SELECT id FROM table2 GROUP BY id) newtable ON table1.id = newtable.id
As both left joins uses data from the same table, is it possible to combine the two joins into one? How would the filters work in this case?
If your goal just to join table2 based on distinct values, then you can use WHERE and GROUP BY:
Select
*
from table1 t1
LEFT JOIN table2 t2
ON t1.name = t2.name and t1.id = t2.id
WHERE t1 id in (SELECT s2.id FROM table2 s2 GROUP BY s2.id)

What type of SQL join is it in which the inner-join is not directly followed by an ON clause?

What is the name of this type of join? I've looked all over! The query is inner-joining back-to-back and then specifying two ON clauses.
Bonus points: What is the benefit of joining this way?
SELECT
<some columns>
FROM
ProductTypes AS t0
INNER JOIN Table1 AS t1
INNER JOIN Table2 AS t2
ON t2.CodeId = t1.CodeId
AND t2.[Enabled] = 1
ON t1.ClassId = t0.ClassId
Still it will be INNER JOIN.
It will interpreted as
SELECT <some columns>
FROM producttypes AS t0
INNER JOIN table2 AS t2
ON t1.classid = t0.classid
INNER JOIN table1 AS t1
ON t2.codeid = t1.codeid
AND t2.[enabled] = 1
Compiler is smart enough to rearrange the JOIN order
Here is demo of what's happening internally. I have used my own table's with similar JOIN order
Your query JOIN order
SELECT
*
FROM
users AS t0
INNER JOIN products AS t1
INNER JOIN orders AS t2
ON t2.productid = t1.productid
AND t2.productid = 1
ON t2.uid = t0.uid
Execution Plan
The rearranged JOIN order
SELECT
*
FROM
users AS t0
INNER JOIN orders AS t2
ON t2.uid = t0.uid
INNER JOIN products AS t1
ON t2.productid = t1.productid
AND t2.productid = 1
Execution Plan
As you can see both has identical execution plan. So there wont be any difference
This is a form of doing nested joins. There's no purpose with all inner unless you want to do it for readability. It can be useful if you use left outer. Consider:
SELECT
<some columns>
FROM
ProductTypes AS t0
LEFT OUTER JOIN Table1 AS t1
INNER JOIN Table2 AS t2
ON t2.CodeId = t1.CodeId
AND t2.[Enabled] = 1
ON t1.ClassId = t0.ClassId
Now this does something. If you did it without putting the t1/t0 at the end the t1/t2 inner would basically negate the fact that t1/t0 is a left outer. So doing it this way lets you have t0 records with no t1 records (just like a normal left outer join), but will only show the t1 records that ALSO have a t2. The inner join is enforced at this lower level.
See also:
http://sqlity.net/en/1435/a-join-a-day-nested-joins/
SQL join format - nested inner joins

Missing data from converting OR condtion in Join to Another JOIN

Few days back I asked how to change OR condtion (left join) into another left join. Here is the code below :
SELECT *
FROM Table1 t1
LEFT JOIN Table2 t2
ON t1.ID = t2.ID
OR (
t1.col1 = t2.col1
AND t1.col2 = t2.col2
AND t1.col3 = t2.col3
AND t2.ID IS NULL
)
I converted to OR condition like below:
SELECT *
FROM Table1 t1
LEFT JOIN Table2 t2
ON t1.ID = t2.ID
LEFT JOIN Table2 t3
ON (
t1.col1 = t3.col1
AND t1.col2 = t3.col2
AND t1.col3 = t3.col3
AND t2.ID IS NULL
)
But i am not getting records for second left join in seperate row.
With OR condition result are like :
T1.ID T2.ID
1 a
2 b
3 c
4 d
But with second left join its lile below:
T1.ID T2.ID
1 a
2 b
3 c
4rth record is from second left join. How can i bring that in left join. I dont have option for using UNION ALl as per company standards.
Please help.
Thanks
First of all please notice that you're using t2 instead of t3 in the second left join :
SELECT *
FROM Table1 t1
LEFT JOIN Table2 t2
ON t1.ID = t2.ID
LEFT JOIN Table2 t3
ON (
t1.col1 = t3.col1
AND t1.col2 = t3.col2
AND t1.col3 = t3.col3
AND t3.ID IS NULL
)
Anyhow, without understanding what you'r trying to do and how do you want to see your result, it's hard to answer you.
You're using left join which just gives you everything from left result set. Since, you want data from t3 which is in right, change second left join to be either right join or full join, depending on your requirements. One of the following query can give you correct result:
Editing 14-03-2015
SELECT *
FROM Table1 t1
FULL OUTER JOIN Table2 t2
ON t1.ID = t2.ID
FULL OUTER JOIN Table2 t3
ON (
t1.col1 = t3.col1
AND t1.col2 = t3.col2
AND t1.col3 = t3.col3
AND t2.ID IS NULL
)

Resources