Convert aggregate SQL query to LINQ - c#

I decided to try my hand at LINQ and so far its been a miserable failure. I need to convert the following SQL query to LINQ:
SELECT
MAX(A.NEXTPAYMENTDATE) as NextPaymentDate
, SUM(B.PurchasePrice) - SUM(A.Amount) AS BALANCE
, c.FirstName
, c.LastName
, b.[year]
, b.make
, b.model
FROM Payments A
JOIN Vehicles B ON A.VehicleId = B.Id
JOIN Customers C ON b.CustomerId = c.Id
GROUP BY VehicleId, c.FirstName, c.LastName, b.[year], b.make, b.model
HAVING SUM(B.PurchasePrice) - SUM(A.Amount) > 0
This is what I have so far. It seems to work to a certain extent, but I don't know how to progress from here.
var groupedpayments =
from payments in db.Payments
group payments by new { payments.VehicleId }
into paymentGroup
let maxDate = paymentGroup.Max(x => x.NextPaymentDate)
let paid = paymentGroup.Sum(x => x.Amount)
select
new { Payments = paymentGroup.Where(x => x.NextPaymentDate == maxDate)};

I think that is what you need.
var query =
Payments.Join(Vehicles, p => p.VehicleId, v => v.Id, (p, v) => new {p, v})
.Join(Customers, d => d.v.CustomerId, c => c.Id, (d, c) => new {d, c})
.GroupBy(r =>
new {
r.d.p.VehicleId,
r.d.v.year,
r.d.v.make,
r.d.v.model,
r.c.FirstName,
r.c.LastName
},
(g, data) =>
new {
FirstName = g.FirstName,
LastName = g.LastName,
Year = g.year,
Make = g.make,
Model = g.model,
NextPaymentDate = data.Max(dd => dd.d.p.NEXTPAYMENTDATE),
Balance = data.Sum(dd => dd.d.v.PurchasePrice)
- data.Sum(dd => dd.d.p.Amount)})
.Where(r => r.Balance > 0);

Related

Linq an aggregate with a grouping

I'm translating a query and here's the original:
select top 5 t.usrID, u.FirstName, u.LastName, t.cnt as sCount
from (
select usrID, COUNT(rID) as cnt
from sessions as s where s.sDate > DATEADD(yy, -1, getdate())
group by usrID
) as t
inner join users as u on t.usrID = u.usrID
order by t.cnt desc
Here's what I have so far:
var topUser = (from p in _context.Sessions
where p.SDate > DateTime.Now.AddYears(-1)
join c in _context.Users on p.UsrId equals c.UsrId into j1
from j2 in j1.DefaultIfEmpty()
// group j2 by p.UsrId into grouped
select new
{
p.UsrId,
j2.FirstName,
j2.LastName,
cnt = p.RId
})
//.OrderBy(d => d.cnt)
//.GroupBy(o => o.UsrId)
.Take(5);
I'm having trouble figuring out how to include count() and group by clauses. When I include groupBy my other columns disappear. Thank you.
This is the answer to your answer - not to your original query. I would put it as comment, but without formatting it's hard to explain
Assuming User object has collection of Session your first statement can be drastically simplified:
var topUsers = _context.Sessions
.Where(s => s.SDate > DateTime.Now.AddYears(-1))
.Select(s => new
{
s.UsrId,
cnt = s.User.Sessions.Count(u => u.UsrId == s.UsrId)
})
.OrderByDescending(s => s.cnt)
.Take(5);
You can shape the results to get a ViewModel that also has FirstName and LastName. It all boils down to defining a model with one-to-many relationship
var topUser = (from p in _context.Sessions
where p.SDate > DateTime.Now.AddYears(-1)
join c in _context.Users on p.UsrId equals c.UsrId into j1
from j2 in j1.DefaultIfEmpty()
group j2 by p.UsrId into g
select new
{
UsrId = g.Key,
FirstName = g.Select(x => x.FirstName).FirstOrDefault(),
LastName = g.Select(x => x.LastName).FirstOrDefault(),
sCount = g.Count()
})
.OrderByDescending(d => d.sCount)
.Take(5);
Alright so it's not the most efficient but it works:
var topUsers = _context.Sessions
.Where(s => s.SDate > DateTime.Now.AddYears(-1))
.GroupBy(s => s.UsrId)
.Select(ws => new { ws.Key, cnt = ws.Count() })
.OrderByDescending(s => s.cnt)
.Take(5);
var topNamedUsers = topUsers.Join(_context.Users, ws => ws.Key, ud => ud.UsrId, (ws, ud) => new { ws.Key, ud.FirstName, ud.LastName, ws.cnt });

Translating Linq expression in c#

I am trying to convert this code:
var query2 = from b in db.MU_Reports
join downtime in db.Downtime_Reports on b.Shift equals downtime.Shift
where downtime.Downtime_Code.Equals("9185")
group downtime by new { b.Date, b.Shift, b.Machine_Number, b.MU } into g
select new
{
Date = g.Key.Date,
Shift = g.Key.Shift,
Machine = g.Key.Machine_Number,
MU = g.Key.MU,
No_Work_Hours = g.Sum(x => x.Total_DownTime)
};
To look something like this one:
var query = db.MU_Reports.Join(db.Downtime_Reports, b=> b.Shift, c=> c.Shift, (b , c) => new { b, thisshift = c })
.Where(n => n.thisshift.Down_TIme_Codes.Equals("9185"))
.GroupBy(d=> new { d.b.Date, d.b.Shift, d.b.Machine_Number, d.b.MU }, d => d.b)
.Select (g=> new
{
Date = g.Key.Date,
Shift = g.Key.Shift,
Machine = g.Key.Machine_Number,
MU = g.Key.MU,
No_Work_Hours = g.Sum(i => i.Total_DownTime)
}).ToList();
As you can see I am very close. My only issue is the last statement No_Work_Hours = g.Sum(i => i.Total_DownTime) It is trying to get the Total_DownTime from db.MU_Reports but it needs to come from db.Downtime_Reports. I am new to c# and am doing this to understand the program I created better.
Your second argument to GroupBy should be d => d.thisshift instead of d => d.b. That corresponds to the group downtime by, but by doing d => d.b it's like you're doing group b by
var query = db.MU_Reports
.Join(
db.Downtime_Reports,
b=> b.Shift,
c=> c.Shift,
(b , c) => new { b, thisshift = c })
.Where(n => n.thisshift.Down_TIme_Codes.Equals("9185"))
.GroupBy(
d=> new { d.b.Date, d.b.Shift, d.b.Machine_Number, d.b.MU },
d => d.thisshift)
.Select (g=> new
{
Date = g.Key.Date,
Shift = g.Key.Shift,
Machine = g.Key.Machine_Number,
MU = g.Key.MU,
No_Work_Hours = g.Sum(i => i.Total_DownTime)
})
.ToList();

Translating my SQL Query to c# linq/lambda. Multiple parameter GroupBy

I've been puzzling over this problem all morning and can't figure out how to do it in C#.
My SQL query as follows:
select a.CourseID,
a.UserID
from audit a
inner join results r on a.UserID = r.UserID
inner join Course c on a.CourseID = c.CourseID
where c.CourseType = 9 and a.Guid = 'A123F123D123AS123123'
and a.Result = 'Passed' and r.Class = 'Maths'
group by a.CourseID, a.UserID
order by a.UserID
returns exactly what I want, but I can't seem to translate it into linq format. (the format being used here is what is required in my job at the moment so please advise on this format)
So far I have the following:
var audits = auditRepository.Get(a => a.Course.CourseType == 9 && a.GUID == this.Company.GUID && a.Result == "Passed", null, null,
a => a.Course, a => a.User)
.Join(resultsRepository.Get(r => r.GUID == this.Company.GUID && r.Class == class),
a => a.UserID,
r => r.UserID,
(a, r) => new Audit
{
User = a.User,
Course = a.Course,
Result = a.Result,
Timestamp = a.Timestamp,
AuditID = a.AuditID,
UserID = a.UserID
}
)
.OrderByDescending(o => o.Timestamp)
.GroupBy(u => new { u.User, u.Course })
.Select(grp => grp.ToList())
.ToList();
However this returns duplicates.
Any advice is appreciated, thanks
H
Instead of
.Select(grp => grp.ToList())
Select only the first element from each group to exclude duplicates:
.Select(grp => grp.First())
If you need a count also:
.Select(t => new{grp = t.First(),cnt = t.Count()} )
Fix:
.Select(t => new { grp = t.First(), cnt = t.Select(s => s.AuditID).Distinct().Count() })

Entity Framework with Linq, inner Join, Group By, Order By

I have a SQL Query
select Firma.Name as companyName,
Taetigkeit.Taetigkeit as skillName,
SUM(Zeit) as time
from Zeiterfassung
inner join Firma On ZEiterfassung.FirmenID = Firma.ID
inner join Taetigkeit on Zeiterfassung.TaetigkeitID = Taetigkeit.ID
group by Taetigkeit, Firma.Name
order by Firma.Name
And want to "translate" it to linq. Here is what I tried:
var query = db.Zeiterfassung
.Where(x => x.Firma.ID == x.FirmenID && x.TaetigkeitID == x.Taetigkeit.ID)
.GroupBy(x => x.Taetigkeit.Taetigkeit1)
.Select(x => new Evaluation() { skillName = x.Key, time = x.Sum(y => y.Zeit), //skillName = x.Sum(x => x.Zeit), })
.OrderBy(x => x.skillName);
I dont know who to solve this with joins and the group by because all the time when i do a groupBy i cant access the other members.
From data you provided, I think query should look like
from z in db.Zeiterfassung
join f in db.Firma on z.FirmenID equals f.ID
join t in db.Taetigkeit on z.TaetigkeitID equals t.ID
select new { f.Name, t.Taetigkeit, z.Zeit) into x
group x by new { x.Taetigkeit, f.Name } into g
select new {
CompanyName = g.Key.Name,
SkillName = g.Key.Taetigkeit,
Time = g.Sum(i => i.Zeit)
}
Or with navigation properties:
db.Zeiterfassung
.Select(z => new { z.Zeit, z.Taetigkeit.Taetigkeit1, z.Firma.Name })
.GroupBy(x => new { x.Taetigkeit1, x.Name })
.Select(g => new Evaluation {
companyName = g.Key.Name,
skillName = g.Key.Taetigkeit1,
time = g.Sum(y => y.Zeit)
});

convert linq to lambda with multiple joins

I'm trying to convert this linq query to lambda
var query = (from a in context.Table_A
join u in context.Table_B on a.scored equals u.userid
join u2 in context.Table_B on a.scorer equals u2.userid
from cs in a.Table_C //(Table_A is related to Table_C)
where (a.date>= startdate && a.date < enddate)
select new MSViewModel
{
scored= u.User.name,
scorer= u2.User.name,
subject_name = cs.Subject.name,
score = cs.score,
categoryid = cs.id,
})
.AsEnumerable()
.GroupBy(t => t.scored)
.ToList();
so far this is what i have. I'm kinda lost what to do next.
var tobi = db.Table_A.Join(db.Table_B,a=>a.scored,u=>u.userid,
(a,u) => new {scored=u.User.name });
db.Table_A.Join(db.Table_B,a1=>a1.scorer,u2=>u2.userid,
(a,u2)=> new {scorer= u2.User.name});
Firstly, I'd agree with Arran's comment: the query expression is going to be much simpler to deal with. I'm absolutely on board with using the lambda form where it makes sense, but joins are generally much simpler in query expressions.
Having said that, you basically need to imitate transparent identifiers. The code below is untested, but looks vaguely plausible to me.
var query = context.Table_A
.Join(context.Table_B, a => a.scored, u => u.userid,
(a, u) => new { a, u })
.Join(context.Table_B, p => p.a.scorer, u2 => u2.userid,
(p, u2) => new { p, u2 })
.SelectMany(q => q.p.a.TableC, (q, cs) => new { q, cs })
.Where(r => r.q.p.a.date >= startdate && r.q.p.a.date < enddate)
.Select(q => new MSViewModel {
scored= r.q.p.u.User.name,
scorer= r.q.u2.User.name,
subject_name = r.cs.Subject.name,
score = r.cs.score,
categoryid = r.cs.id,
})
.AsEnumerable()
.GroupBy(t => t.scored)
.ToList();
Basically p and q and r are the transparent identifiers here. You've got three of them because you've got two joins and a subsequent from clause.
To expand on my comment, If you have Resharper:
Resharper showing option to convert LINQ to Lambada
After the conversion:
Try this one:
var query = (_context.Table_A.Join(_context.Table_B, a => a.scored, u =>
u.userid,
(a, u) => new {a, u}).Join(_context.Table_B,
#t => #t.a.scorer,
u2 => u2.userid,
(#t, u2) => new {#t, u2}).
SelectMany(#t => _context.Table_A, (#t, cs) => new MSViewModel()
{
scored = #t.#t.u.User.name,
scorer= #t.u2.User.name,
subject_name = #t.cs.Subject.name,
score = #t.cs.score,
categoryid = #t.cs.id,
})).AsEnumerable()
.ToList();

Categories

Resources