问题
I wrote this SQL request with multiple JOIN (including a LEFT JOIN).
It gives me the expected result.
SELECT DISTINCT c.Id,
c.Title,
COUNT(v.Id) AS \'Nb_V2\',
COUNT(DISTINCT v.IdUser) AS \'Nb_V1\',
r.cnt AS \'Nb_R\'
FROM TABLE_C c
JOIN TABLE_V v on c.Id = v.Id
LEFT JOIN (
SELECT Id, COUNT(*) AS cnt
FROM TABLE_R
GROUP BY Id
) r ON c.Id = r.Id
WHERE c.IdUser = \'1234\'
GROUP BY c.Id, c.Title, r.cnt
However, \'Id like the Linq equivalent of this request, to put it my application\'s Data Access layer.
I tried something like :
var qResult = from c in dbContext.TABLE_C
join v in dbContext.TABLE_V on c.IdC equals v.IdC
join r in dbContext.TABLE_R on v.IdC equals r.IdC into temp
from x in temp.DefaultIfEmpty()
group x by new { c.IdC, c.Title /*miss something ?*/} into grouped
select new
{
IdC = grouped.Key.IdC, --good result
Title = grouped.Key.Title, --good result
NbR = grouped.Distinct().Count(t => t.IdC > 0), --good, but \"t.Id > 0\" seems weird
Count = --I\'m lost. No idea how to get my COUNT(...) properties (Nb_V1 and Nb_V2)
};
I tried to adapt this SO question but I can\'t figure it out. I\'m lost with the Count inside the groupped sub-request.
Can anyone explain me where i\'m wrong ?
Pro tip : Bonus point if someone can write the equivalent with a lambda expression
回答1:
For translating SQL to LINQ query comprehension:
- Translate subselects as separately declared variables.
- Translate each clause in LINQ clause order, translating monadic and aggregate operators (
DISTINCT,TOP,MIN,MAXetc) into functions applied to the whole LINQ query. - Use table aliases as range variables. Use column aliases as anonymous type field names.
- Use anonymous types (
new {...}) for multiple columns (e.g. ingroupby). - Use
First().fieldto get non-key values from thegroupbyaggregate range variable. JOINconditions that are multipleANDed equality tests between the two tables should be translated into anonymous objects on each side ofequalsJOINconditions that aren't all equality tests withANDmust be handled usingwhereclauses outside the join, or with cross product (from...from...) and thenwhere. If you are doingLEFT JOIN, add a lambdaWhereclause between the join range variable and theDefaultIfEmpty()call.LEFT JOINis simulated by usingintojoinvariable and doing anotherfromthe joinvariable followed by.DefaultIfEmpty().- Replace
COALESCEwith the conditional operator (?:)and anulltest. - Translate
INto.Contains()andNOT INto!...Contains(), using literal arrays or array variables for constant lists. - Translate x
BETWEENlowANDhigh to low<=x&&x<=high. - Translate
CASEandIIFto the ternary conditional operator?:. SELECT *must be replaced with select range_variable or for joins, an anonymous object containing all the range variables.SELECTcolumns must be replaced withselect new {...}creating an anonymous object with all the desired fields or expressions.- References to computed
SELECTcolumns can be translated by repeating the expression or by usingletto name the expression before its first use. - Proper
FULL OUTER JOINmust be handled with an extension method. - Translate
UNIONtoConcatunless both sub-queries areDISTINCT, in which case you can translate toUnionand leave off theDISTINCT.
Applying these rules to your SQL query, you get:
var subrq = from r in Table_R
group r by r.Id into rg
select new { Id = rg.Key, cnt = rg.Count() };
var ansq = (from c in Table_C
join v in Table_V on c.Id equals v.Id
join r in subrq on c.Id equals r.Id into rj
from r in rj.DefaultIfEmpty()
where c.IdUser == "1234"
group new { c, v, r } by new { c.Id, c.Title, r.cnt } into cvrg
select new {
cvrg.Key.Title,
Nb_V2 = cvrg.Count(),
Nb_V1 = cvrg.Select(cvr => cvr.v.IdUser).Distinct().Count(),
Nb_R = (int?)cvrg.Key.cnt
}).Distinct();
The lambda translation is tricky, but the conversion of LEFT JOIN to GroupJoin...SelectMany is what is needed:
var subr2 = Table_R.GroupBy(r => r.Id).Select(rg => new { Id = rg.Key, cnt = rg.Count() });
var ans2 = Table_C.Where(c => c.IdUser == "1234")
.Join(Table_V, c => c.Id, v => v.Id, (c, v) => new { c, v })
.GroupJoin(subr, cv => cv.c.Id, r => r.Id, (cv, rj) => new { cv.c, cv.v, rj })
.SelectMany(cvrj => cvrj.rj.DefaultIfEmpty(), (cvrj, r) => new { cvrj.c, cvrj.v, r })
.GroupBy(cvr => new { cvr.c.Id, cvr.c.Title, cvr.r.cnt })
.Select(cvrg => new { cvrg.Key.Title, Nb_V2 = cvrg.Count(), Nb_V1 = cvrg.Select(cvr => cvr.v.IdUser).Distinct().Count(), Nb_R = (int?)cvrg.Key.cnt });
来源:https://stackoverflow.com/questions/49245160/sql-to-linq-with-multiple-join-count-and-left-join