LINQ and joining on selects - c#

I have the following SQL:
select o.tekst as Enhet,
coalesce(f.Antall,0) as AntallF,
coalesce(f.snitt,0) as SnittF,
coalesce(b.antall,0) as AntallB
from tblhandlingsplan hp
inner join tblorg o on hp.eierorgid = o.orgid
left outer join (select f.handlingsplanid, count(t.tiltakid) as Antall, coalesce(avg(convert(float,t.status)),0) as Snitt from tblhandlingsplanforbedring f left outer join tblhandlingsplantiltak t on f.forbedringsid = t.forbedringsid group by f.handlingsplanid) f on hp.handlingsplanid = f.handlingsplanid
left outer join (select b.handlingsplanid, count(b.bevaringsid) as Antall from tblhandlingsplanbevaring b group by b.handlingsplanid) b on hp.handlingsplanid = b.handlingsplanid
where utsendingsid = 1
Which works exactly how I want it... Now I'm trying to convert this to LINQ...
I have gotten this far
from h in TblHandlingsplans
join o in TblOrgs
on h.EierOrgID equals o.OrgID
join f in TblHandlingsplanForbedrings
on h.HandlingsplanID equals f.HandlingsplanID into f2
join b in TblHandlingsplanBevarings
on h.HandlingsplanID equals b.HandlingsplanID into b2
where h.UtsendingsID == 1
select new {
Enhet = o.Tekst,
AntallF = f2.Count(),
AntallB = b2.Count()
}
however now I'm stuck... I can't for the life of me figure out how to include the average part from the SQL solution... Any takers?
I'm thinking of shoving the whole thing into a SP and leave it with that...

var query1 = from a in DB.Table1
select new
{
Id = a.Id,
Average = a.B.Average()
};
var query2 = from b in DB.Table2
join c in query1 on b.Id equals c.Id
select c;
Just freehanded it, so it might not actually work, but is that the kind of thing you're trying to do? That would result in a single SQL query being created when query2 was used.

Related

SQL to LINQ with outer apply

I am not able convert sql to linq because of outer apply
select * from dbo.Table1 l
inner join dbo.Table2 d on d.LoanId = l.Id
inner join dbo.Cash cOriginal on cOriginal.DealId = d.Id and cOriginal.IsOriginal = 1
outer apply (select top 1 * from dbo.Cash cActive
where cActive.DealId = d.ID and cActive.IsOriginal = 0
order by cActive.CreatedOn desc) cActiveRes
I am started something like this:
var q = from x in _repo.Queryable<Table1>()
join Table2 in _repo.Queryable<Table2>() on x.Id equals Table2.LoanId
join cOriginal in _repo.Queryable<Cash>() on Table2.Id equals
cOriginal.DealId
// now Outer Apply should come?
// fActive in _repo.Queryable<Cash>()
The easiest way it to run your SQL query directly letting EF to materialize the result. It might look like this (depending on version of EF you use):
var result = ((IObjectContextAdapter)_repo).ObjectContext.ExecuteStoreQuery<Table1>(
#"SELECT your query here");

How can I write this query in LINQ

I have this query
SELECT t.NomeTipo, sum(v.QtdProduto)
FROM [dbo].[Vendas] AS V
RIGHT JOIN [dbo].[Produtos] AS P ON V.IdProduto = P.IdProduto
INNER JOIN [dbo].[Tipos] AS T ON P.IdTipo = T.IdTipo
group by t.NomeTipo
order by t.NomeTipo
I have tried this
var queryTipos = from vendas in repositorioVendas.Vendas
join produtos in repositorioProduto.Produtos.DefaultIfEmpty()
on vendas.IdProduto equals produtos.IdProduto
join tipos in repositorioTipo.Tipos
on produtos.IdTipo equals tipos.IdTipo
group vendas by new { tipos.NomeTipo, vendas.QtdProduto }
into novoGrupo
select new
{
NomeTipo = novoGrupo.Key.NomeTipo,
QtdProduto = novoGrupo.Sum(x => x.QtdProduto)
};
With this query I got only two results, but when I run straight from the database I get something like this:
Bebidas 16
Bolos 14
Frios 16
Pães 21
The trick is to realize that you can rewrite your query with a left join instead of a right join by swapping the order of the first two tables and that Linq doesn't have a way to really handle right joins. Also you're grouping was wrong.
var queryTipos = from produtos in repositorioProduto.Produtos
join vendas_pj in repositorioVendas.Vendas
on vendas_pj.IdProduto equals produtos.IdProduto into joined
from vendas in joined.DefaultIfEmpty()
join tipos in repositorioTipo.Tipos
on produtos.IdTipo equals tipos.IdTipo
group vendas by tipos.NomeTipo
into novoGrupo
select new
{
NomeTipo = novoGrupo.Key,
QtdProduto = novoGrupo.Sum(x => x.QtdProduto)
};
Basically a Left join in SQL
From TableA
Left Join TableB
On TableA.ID = TableB.ID
is done in Linq like this
from a in repo.TableA
join b_pj in repo.TableB
on a.ID equals b_pj.ID into joined
from b in joined.DefaultIfEmpty()

How to write LINQ left join

var query = from r in db.Resource
join c in db.ResourceProjectedCapacity on r.ID equals c.ResourceID into ps
from c in ps.DefaultIfEmpty(null)
join p in db.Project on c.ProjectID equals p.ID
select new
{
Capacity = c,
Resource = r,
Project = p
};
I have this linq query but it is only returning resources that have a matching row on ResourceProjectedCapacity table. How can I get all resources and in case they dont have a matching record the Capacity object to be null?
from i in db.Resource
let c = db.ResourceProjectedCapacity.Where(cc => i.id == cc.ResourceID).FirstOrDefault()
let p = db.Project.Where(pp => c.ProjectID == pp.ID).FirstOrDefault()
select new
{
Capacity = C,
Resource = i,
Project = p
}
try above code
I think the secondary inner join messes up the left outer join above it. I think a way to work around it is to break the join into a seperate query, and then left join on that, something like this:
var subquery = from c in db.ResourceProjectedCapacity
join p in db.Project on c.ProjectID equals p.ID
select new { c, p };
var query = from r in db.Resource
join c in subquery on r.ID equals c.c.ResourceID into ps
from c in ps.DefaultIfEmpty(null)
select new
{
Capacity = c.c,
Resource = r,
Project = c.p
};
NB don't worry, it won't do two db queries, it'll only execute against your db once you evaluate query with a .ToList() or something like that.

Groupby entity joined with another Table

I have this scenario that I didnt find any solution looking through related SO questions :
(from TD as tx
join P as px on tx.field1= px.ID
join Q as rp on tx.field2 = rp.ID
join L as lc on tx.field3= lc.ID
group by tx.field1,
tx.field2,
L.randomfield4,
....a bunch of fields from P,Q,L
)as groupItem
left outer join M on groupItem.field1=M.ID
select new { */ Elements from groupItem and M /*}
My Linq looks as follows :
from tx in TD
join itemP in P on tx.field1 equals P.ID
join itemQ in Q on tx.field2 equals P.ID
join itemL in L on tx.field3 equals P.ID
group new { tx.field1 ,tx.field2 ,L.randomfield4 } by new { **fields from tx,p,q,etc} into groupItem
join dM in M on ????
If i try to select elements from groupItems I cannot access proprieties (because i didnt select anything ).
Can anyone help me with a starting point to this issue ? Also help me with a better name for the question :)
Hopefully I understood your question correctly but here is my answer. I divided the query into two so its easy to understand just combine them together if you fully get into it.
Basically the variable "groupedCollection" is your first joins and your grouping which basically includes the following collections TD, P, Q and L. Then the second variable "finalCollection" is your left outer join which give the result you need.
var groupedCollection = from tx in TD
join itemP in P on tx.field1 equals itemP.ID
join itemQ in Q on tx.field2 equals itemQ.ID
join itemL in L on tx.field3 equals itemL.ID
group new
{
//Select the collections you want to group it by
P,
Q,
L,
TD
}
by new
{
//Select fields you want to output group by here
tx.field1,
tx.field2,
tx.field3,
itemL.RandomField4
}
into g
select new
{
//Use ky to get your properties as they are already grouped by
yourField1 = g.Key.field1,
yourField2 = g.Key.field2,
yourField3 = g.Key.field3,
yourField4 = g.Key.RandomField4
};
var finalCollection = from dM in M
//This is your Left Outer Join
join xx in groupedCollection on dM.ID equals xx.yourField1 into sr
from w in sr.DefaultIfEmpty()
//Until here
select new
{
finalResultID = dM.ID,
finalField1 = w.yourField1,
finalField2 = w.yourField2,
finalField3 = w.yourField3,
finalField4 = w.yourField4
};
for more LINQ stuff this will give you an idea
http://anyrest.wordpress.com/2010/09/27/linq-to-sql-essentials/

How to Implement this Sql block contain (Having , Join and Group By) with Linq

my sql statement is
SELECT c.type,c.title,c.datereg, d.ranknum
FROM T_News AS c
INNER JOIN (
SELECT a.id, COUNT(*) AS ranknum
FROM T_News AS a
INNER JOIN T_News AS b
ON (a.type = b.type)
AND (a.datereg >= b.datereg)
GROUP BY a.id
HAVING COUNT(*) <= 3
) AS d ON (c.id = d.id)
ORDER BY c.type, d.ranknum
that i get http://rickosborne.org/blog/2008/01/sql-getting-top-n-rows-for-a-grouped-query/
for Getting TOP N rows for a grouped query
EFUnitOfWork EF = new EFUnitOfWork();
T_NewsRepository News = new T_NewsRepository();
News.UnitOfWork = EF;
var query =
from news1 in News.All()
join news2 in News.All()
on news1.type equals news2.type into resjoin
group news1 by news1.id into idgroup
where idgroup.Count() <= 3
select new { idgroup };
var x = query.ToList();
I did not get any error , but "where idgroup.Count() <= 3" did not work and i get all rows in db as result
Break it down into it's smallest components and then compose the larger query from that. Let's start with the innermost query that makes sense:
SELECT
a.id, COUNT(*) AS ranknum
FROM
T_News AS a
INNER JOIN T_News AS b ON
(a.type = b.type) AND
(a.datereg >= b.datereg)
GROUP BY
a.id
HAVING
COUNT(*) <= 3
I'd convert this to:
// Items with counts/ranknum
var ranknum =
from a in News.All()
join b in News.All() on
a.type equals b.type
where
a.datereg > b.datereg
group by a.id into g
select new { g.Key as id, g.Count() as ranknum };
// Filter the ranknum.
ranknum = ranknum.Where(rn => rn.ranknum <= 3);
Then joining that with the outer query:
SELECT
c.type,c.title,c.datereg, d.ranknum
FROM
T_News AS c
INNER JOIN (<sub-query from above>) as d ON
c.id = d.id
ORDER BY
c.type, d.ranknum
That part becomes simple, as it's just a join between two existing queries.
var query =
from c in News.All()
join rn in ranknum on c.id = rn.id
orderby c.type, rn.ranknum
select new { c.type, c.title, c.datereg, rn.ranknum };
Chances are the SQL that LINQ-to-Entities generates for this is going to look really ugly, and probably be inefficient, in which case, you might want to consider placing this logic in a stored procedure and then calling that through LINQ-to-Entities (which is generally true for more complex queries).

Categories