Transform sql query to linq with groupBy and months - c#

I have following query:
select concat(Left(DateName(month,[date]),3), ' ', Year([date])),
sum(TotalAttendants) as Total,
Sum(FemaleAttendants) as Women,
Sum(MaleAttendants) as Men
from dbo.Events
where IsDeleted=0 and EventTypeId = 1
group by concat(Left(DateName(month,[date]),3), ' ', Year([date]))
and I want to transform it to c# linq lambda expression.
I tried something like this:
var response = await _context.Events
.Where(x => !x.IsDeleted && x.EventTypeId == Domain.Enums.EventTypes.DirectBeneficiaries)
.GroupBy(x => x.Date)
.Select(x => new EventViewData
{
MaleAttendants = x.Sum(u => u.MaleAttendants),
FemaleAttendants = x.Sum(u => u.FemaleAttendants),
TotalAttendants = x.Sum(u => u.TotalAttendants),
MonthName = x.Key.ToString("00")
}).ToListAsync();
Im not getting same result as Im getting in my mssql management studio.
If you need more information about data structure and table Events here is the my another stackoverflow topic: link

I think you should group by month and year and do the formatting (concat, etc.) later (if needed at all).
select
...
from dbo.Events
..
group by Month([date]), Year([date]))
Then in linq you can:
...
.GroupBy(x => new { Year = x.Date.Year, Month = x.Date.Month } )
.Select(x => new // Note no type name
{
MaleAttendants = x.Sum(u => u.MaleAttendants),
FemaleAttendants = x.Sum(u => u.FemaleAttendants),
TotalAttendants = x.Sum(u => u.TotalAttendants),
Month = x.Key.Month,
Year = x.Key.Year
})
.ToListAsync() // Hit the db
.Select( x => new EventViewData
{
x.MaleAttendants
x.FemaleAttendants
x.TotalAttendants
MonthName = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.GetAbbreviatedMonthName(x.Month)
...
}
I don't think GetAbbreviatedMonthName is supported by EF so we need to do it after ToListAsync.

Related

Entity framework complex query to nested C# object

I am having following query here. how do I get similar linq query for this sql.
SELECT *
FROM PublishedLineBar
WHERE PublishedRosterShiftId
IN (SELECT LatestShiftId FROM
( SELECT MAX(PublishedRosterShiftId) as LatestShiftId, DayNumber
FROM PublishedRosterShift
WHERE employeeid = 14454
GROUP BY DayNumber)
as ShiftProjection )
I have used below linq translation, but it is failing somewhere.
var shifts = dbContext.PublishedRosterShifts
.Where(h => h.EmployeeId == EmployeeId);
var inner = shifts
.Select(x => new
{
LatestShiftId = shifts.Max(p => p.PublishedRosterShiftId),
DayNumber = x.DayNumber
})
.GroupBy(s => s.DayNumber)
.Select(g => g.FirstOrDefault());
var q = from f in shifts
select new
{
LatestShiftId = shifts.Max(p => p.PublishedRosterShiftId),
DayNumber = f.DayNumber
};
var query = from l in dbContext.PublishedLineBars
where inner.Select(s => s.LatestShiftId).Contains(l.PublishedRosterShiftId)
select l;
Here is the LINQ equivalent of your subquery used for SQL IN (...) clause (with unnecessary nesting removed):
var inner = dbContext.PublishedRosterShifts
.Where(s => s.EmployeeId == EmployeeId)
.GroupBy(s => s.DayNumber)
.Select(g => g.Max(s => s.PublishedRosterShiftId));
and the query using it:
var query = from l in dbContext.PublishedLineBars
where inner.Contains(l.PublishedRosterShiftId)
select l;
or simply
var query = dbContext.PublishedLineBars
.Where(l => inner.Contains(l.PublishedRosterShiftId));
What you are missing in your attempt is that in SQL SELECT MAX(PublishedRosterShiftId) as LatestShiftId, DayNumber operates on the result of the GROUP BY operator, hence in LINQ the Select should be after GroupBy.

Grouping earliest entry for each day using Linq

Trying to get my head around Linq, and at the same time keep track of the time I log on in the morning, which should be the time I get into the office thereabouts.
My code so far is:
EventLog SecurityLog = new EventLog("Security");
var AccountLoggedOnEntries = SecurityLog.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.Select(x => new
{
DateGenerated = x.TimeGenerated.ToShortDateString()
,
TimeGenerated = x.TimeGenerated.ToShortTimeString()
,
x.Message
})
.ToList();
DgvLogSummary.DataSource = AccountLoggedOnEntries;
DgvLogSummary.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
I want to filter the results so that I only have one entry for each day, which is the earliest time.
In SQL I would normally take the Message of the earliest entry and then group by all fields.
How do I perform a similar query in Linq?
In LINQ you would group by, sort each group, and pick the first item:
var AccountLoggedOnEntries = log.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.GroupBy(x => x.TimeGenerated.Date)
.Select(g => g.OrderBy(x => x.TimeGenerated).First())
.Select(x => new {
DateGenerated = x.TimeGenerated.ToShortDateString()
, TimeGenerated = x.TimeGenerated.ToShortTimeString()
, x.Message
})
.ToList();
You could GroupBy the date and then select the minimum time
var AccountLoggedOnEntries = log.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.GroupBy(x => x.TimeGenerated.Date)
.Select(x => new {
DateGenerated = x.Key
, TimeGenerated = x.Min(y => y.TimeGenerated).ToShortTimeString()
})
.ToList();
Getting the appropriate Message is a little more tricky. One easy option is to use x.First().Message in the above Select projection.
Try this :
var AccountLoggedOnEntries = log.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.GroupBy(x => x.TimeGenerated.Date)
.Select(days => days.OrderBy(time => time.TimeGenerated).FirstOrDefault())
.Select(x => new
{
DateGenerated = x.TimeGenerated.ToShortDateString()
,
TimeGenerated = x.TimeGenerated.ToShortTimeString()
,
x.Message
})
.ToList();

Converting SQL to Linq with groupby, sum and count

I would like to do a group by and on that a sum and a count. I don't seem to be able to create the solution in linq. How can I convert my query to linq?
SELECT HistoricalBillingProductGroup,
COUNT(*),
BillingPeriod,
SUM(TotalMonthlyChargesOtcAndMrc)
FROM [x].[dbo].[tblReport]
group by BillingPeriod, HistoricalBillingProductGroup
order by BillingPeriod
This is what I got sofar in Linq
var result =
context.Reports.GroupBy(x => new {x.BillingPeriod, x.HistoricalBillingProductGroup})
.Select(x => new StatisticsReportLine
{
HistoricalBillingGroup = x.FirstOrDefault().HistoricalBillingProductGroup,
BillingPeriod = x.FirstOrDefault().BillingPeriod,
CountOfRows = x.Count(),
SumOfAmount = x.Sum(p => p.TotalMonthlyChargesOtcAndMrc) ?? 0
})
.ToString();
The query I get from this is enormous and takes a very long time to load. In SQL its a matter of milliseconds. I hardly doubt this is the solution.
I believe the calls to x.FirstOrDefault() are the source of your problem. Each one of these will result in a very costly inner query inside the SELECT clause of the generated SQL.
Try using the Key property of the IGrouping<T> instead :
var result = context.Reports
.GroupBy(x => new {x.BillingPeriod, x.HistoricalBillingProductGroup})
.OrderBy(x => x.Key.BillingPeriod)
.Select(x => new StatisticsReportLine
{
HistoricalBillingProductGroup = x.Key.HistoricalBillingProductGroup,
BillingPeriod = x.Key.BillingPeriod,
CountOfRows = x.Count(),
SumOfAmount = x.Sum(p => p.TotalMonthlyChargesOtcAndMrc) ?? 0
});
Or if you prefer query syntax:
var result =
(from r in context.Reports
group r by new { r.BillingPeriod, r.HistoricalBillingProductGroup } into g
orderby g.Key.BillingPeriod
select new StatisticsReportLine
{
HistoricalBillingProductGroup = g.Key.HistoricalBillingProductGroup,
BillingPeriod = g.Key.BillingPeriod,
CountOfRows = g.Count(),
SumOfAmount = x.Sum(p => p.TotalMonthlyChargesOtcAndMrc) ?? 0
});
You could try this one:
var result = context.Reports
.GroupBy(x => new {x.BillingPeriod, x.HistoricalBillingProductGroup})
.Select(x => new StatisticsReportLine
{
HistoricalBillingGroup = x.Key.HistoricalBillingProductGroup,
BillingPeriod = x.Key.BillingPeriod,
CountOfRows = x.Count(),
SumOfAmount = x.Sum(p => p.TotalMonthlyChargesOtcAndMrc) ?? 0
}).ToString();
In the above query you make a group by on two properties, BillingPeriod and HistoricalBillingProductGroup. So in each group that will be created, you will have a key, that will be consisted by these two properties.

Translating SQL to lambda with groupby

I'm trying to translate this sql statement
SELECT row, SUM(value) as VarSum, AVG(value) as VarAve, COUNT(value) as TotalCount
FROM MDNumeric
WHERE collectionid = 6 and varname in ('C3INEV1', 'C3INEVA2', 'C3INEVA3', 'C3INVA11', 'C3INVA17', 'C3INVA19')
GROUP BY row
into an EF 4 query using lambda expressions and am missing something.
I have:
sumvars = sv.staticvararraylist.Split(',');
var aavresult = _myIFR.MDNumerics
.Where(r => r.collectionid == _collid)
.Where(r => sumvars.Contains(r.varname))
.GroupBy(r1 =>r1.row)
.Select(rg =>
new
{
Row = rg.Key,
VarSum = rg.Sum(p => p.value),
VarAve = rg.Average(p => p.value),
TotalCount = rg.Count()
});
where the staticvararraylist has the string 'C3INEV1', 'C3INEVA2', 'C3INEVA3', 'C3INVA11', 'C3INVA17', 'C3INVA19' (without single quotes) and the _collid variable = 6.
While I'm getting the correct grouping, my sum, average, & count values aren't correct.
You didn't post your error message, but I suspect it's related to Contains. I've found that Any works just as well.
This should get you quite close:
var result =
from i in _myIFR.MDNumerics
where i.collectionid == _collid && sumvars.Any(v => i.varname == v)
group i by i.row into g
select new {
row = g.Key,
VarSum = g.Sum(p => p.value),
VarAve = g.Average(p => p.value),
TotalCount = g.Count()
};
Try this:
var aavresult = _myIFR.MDNumerics
.Where(r => r.collectionid == _collid && sumvars.Contains(r.varname))
.GroupBy(r1 =>r1.row,
(key,res) => new
{
Row = key,
VarSum = res.Sum(r1 => r1.value),
VarAve = res.Average(r1 => r1.value),
TotalCount = res.Count()
});

LINQ with SUM for TimeSpan, GROUP and JOIN for EF [duplicate]

This question already has answers here:
Results from LINQ with SUM for TimeSpan, GROUP and JOIN [closed]
(2 answers)
Closed 9 years ago.
In my EF model:
I make this view:
with following LINQ query (with your help):
var query = ctx.data.ToList().OrderBy(d => d.Time).
GroupBy(d => d.Members.StepId).
SelectMany(g => g.Select((d, place) => new { Time = d.Time, Members = d.Members, PlaceInStep = place + 1 })).
GroupBy(d => d.Members.TeamId).
Select(g => new
{
TeamId = g.Key,
Name = g.Select(d => d.Members.Teams.TeamName).First(),
Members = g.Select(d => new {Time = d.Time, PlaceInStep = d.PlaceInStep, MemberName = d.Members.MemberName}),
TotalTime = g.Aggregate(new TimeSpan(), (sum, nextData) => sum.Add(nextData.Time))
});
But I've two issues:
1. If 2 members in same Step (for ex. before 35 year old) have same time up to milliseconds they must have same place
2. In Members subcollection in result I want to arrange members by StepId and show StepName.
How to modify this query? Thanks.
Edit 1:
I want to take some this:
SQL Query to calculate correct member place:
select *, DENSE_RANK() over (partition by StepId order by Time)
from Members m
join Data d on m.MemberId =d.MemberId
linq equivalent adopted to existing linq query:
var query = context.Data.ToList()
.OrderBy(d=>d.Time)
.GroupBy(d => d.Members.StepId)
.SelectMany(g =>
g.OrderBy(d => d.Time)
.GroupBy(d => d.Time)
.SelectMany((x, place) =>
x.Select(d => new { Time = d.Time, Members = d.Members, PlaceInStep = place + 1 })))
.GroupBy(d => d.Members.TeamId)
.Select(g => new
{
TeamId = g.Key,
Name = g.Select(d => d.Members.Teams.TeamName).First(),
Members = g.Select(d =>
new
{
Time = d.Time,
PlaceInStep = d.PlaceInStep,
MemberName = d.Members.MemberName,
StepName = d.Members.Steps.StepName,
StepId = d.Members.Steps.StepId
}).OrderBy(x=>x.StepId),
TotalTime = g.Aggregate(new TimeSpan(), (sum, nextData) => sum.Add(nextData.Time))
});

Categories

Resources