I'm trying to get the total average count of row instances by day of week. So over the past year, I'm trying to get the average amount of rides that happened on a monday/tuesday/wed/ect.
Here's what I have so far. That gives me the total count per day of week, but not the average count.
UnitOfWork.Query<WorkoutRecord>()
.Where(x => x.WorkoutDate > baseDate)
.GroupBy(x => SqlFunctions.DatePart("weekday", x.UploadDate))
.Select(x => new AdminDashboardWorkoutsGroupedDay()
{
DayOfWeek = (DayOfWeek)x.Key,
WorkoutCount = x.Count()
}).ToList();
If I understand you well, in the end you're trying to get one number, the average count per weekday. This requires a second grouping that reduces the data to one group:
UnitOfWork.Query<WorkoutRecord>()
.Where(x => x.WorkoutDate > baseDate)
.GroupBy(x => SqlFunctions.DatePart("weekday", x.UploadDate))
.Select(x => new AdminDashboardWorkoutsGroupedDay()
{
DayOfWeek = (DayOfWeek)x.Key,
WorkoutCount = x.Count()
})
.GroupBy(g => 0) // or g => "x", or whatever
.Select (g1 => (decimal)g1.Sum(x => x.WorkoutCount) / g1.Count())
.ToList();
Here's what worked. Thanks #Gert Arnold, you got me really close. I had to group by day and count all the workouts, then take the average of that grouping by weekday.
UnitOfWork.Query<WorkoutRecord>()
.Where(x => x.WorkoutDate > baseDate && x.TotalTicks > 600)
.GroupBy(x => EntityFunctions.TruncateTime(x.UploadDate))
.Select(x => new
{
Date = x.Key ?? DateTime.UtcNow,
TheCount = x.Count()
})
.GroupBy(x=> SqlFunctions.DatePart("weekday", x.Date))
.Select (x => new AdminDashboardWorkoutsGroupedDay()
{
WorkoutCount = (x.Average(y=>y.TheCount)),
DayOfWeek = (DayOfWeek)x.Key
})
.ToList()
It's not clear what property you want the average of, but assuming you have a property like WorkoutRecord.Duration, you should be able to get the average like:
UnitOfWork.Query<WorkoutRecord>()
.Where(x => x.WorkoutDate > baseDate)
.GroupBy(x => SqlFunctions.DatePart("weekday", x.UploadDate))
.Select(x => new AdminDashboardWorkoutsGroupedDay()
{
DayOfWeek = (DayOfWeek)x.Key,
AverageDuration = x.Average(w => w.Duration)
})
.ToList();
Related
I have a Customers and an Orders database.
I need to make some statistics for the first order of all new customers and count the number of first orders from new clients by month.`
var date = new DateTime(now.Year - 1, now.Month, 1);
db.Orders
.Where(o => o.Customer.IsNew && o.OrderDate > date)
.GroupBy(o => new { o.OrderDate.Year, o.OrderDate.Month })
.Select(g => new NewCustomerStatsModel {
Month = g.Key.Month,
Year = g.Key.Year,
Count = g.Count()
})
.OrderBy(cs => cs.Year)
.ThenBy(cs => cs.Month)
.ToList();
This query provide me the number of orders for all new client but I need to get only the sum of the first order for each new Customer if the first order date is greater than the provided date.
Is it possible to do it with a query (and how) or am I forced to use AsEnumerable and do it in memory?
I need to make some statistics for the first order of all new customers
var clientFirstOrders = db.Customers.Where(c => c.IsNew)
.Select(c => new{
Customer = c,
FirstOrder = c.Orders.OrderBy(c => c.OrderDate).FirstOrDefault()
})
// might have to do (int?)FirstOrder.Id != null or something like that.
.Where(e => e.FirstOrder != null);
and count the number of first orders from new clients by month.
var clientCountByFirstOrderMonth = clientFirstOrders
.GroupBy(e => new { e.FirstOrder.OrderDate.Year, e.FirstOrder.OrderDate.Month })
.Select(g => new{g.Key.Year, g.Key.Month, Count = g.Count()});
I could find the solution.
With some appropriate index, the performances are pretty good.
It's probably not a perfect solution, but I couldn't update the entities because it's not my Library.
var date = new DateTime(now.Year - 1, now.Month, 1);
var result = db.Orders
.Where(o => o.Customer.IsNew && o.State != OrderState.Cancelled) // get all orders where the Customer is a new one.
.GroupBy(o => o.Customer.Id) // group by customer
.Select(g => g.OrderBy(o => o.OrderDate).FirstOrDefault()) // get the first order for every customer
.Where(o => o.OrderDate > date) // restrict to the given date
.GroupBy(o => new { o.OrderDate.Year, o.OrderDate.Month) }) // then group by month
.Select(g => new NewCustomerStatsModel {
Month = g.Key.Month,
Year = g.Key.Year,
Count = g.Count()
})
.OrderBy(g => g.Year)
.ThenBy(g => g.Month)
.ToList();
I'm trying to figure out how to count the number of Mondays, Tuesdays etc in a table using Linq and C#
Here is my sample data:
Status StatusDate
DELIVRD 2015-04-16 11:57:47.000
DELIVRD 2015-04-16 13:02:57.000
I know I need to use Group by to group the same Mondays, Tuesdays etc as 1.
My attempt:
var mondays = rad.SMSSentItems
.Where(x => (x.StatusDate.Value.DayOfWeek == DayOfWeek.Monday)
&& (x.Status == "DELIVRD"))
.ToList()
.Count;
You need to filter by the desired Status (DELIVRD) then group them by DayOfWeek of the status date
var weekDays = rad.SMSSentItems
.Where(x => x.Status == "DELIVRD")
.AsEnumerable()
.GroupBy(x => x.StatusDate.Value.DayOfWeek)
.Select(g => {
//Total items sent on this day of the week
var totalItemCount = g.Count();
//Total number if this distinct day of the week
var totalNumberOfDays = g.Select(x => x.StatusDate.Value.Date).Distinct().Count();
return new {
DayOfWeek = g.Key,
TotalItemCount = totalItemCount,
TotalNumberOfDays = totalNumberOfDays,
AverageItemPerDay = totalItemCount / totalNumberOfDays
};
})
.ToList();
I have created a dashboard that all data displayed on it shares 4 common elements (startDate,endDate,CompanyID,StoreID) that are used as Where clauses in a Linq statement. The result of that statement is then queried in a variety of ways to group and sort the data and used in charts, lists etc. Here is a short snippit to show the duplication that is currently going on:
var dashboardEntity = new BlueStreakSalesDWEntities();
//Get Total Sales
ViewBag.companySalesTotal = dashboardEntity.FactSales.Where(d => d.DateKey >= startDate)
.Where(d => d.DateKey <= endDate)
.Where(c => c.CompanyID == companyID)
.Sum(a => a.Amount);
//get list of all items sold
var companyStoreTotalItem = dashboardEntity.FactSales.Where(d => d.DateKey >= startDate)
.Where(d => d.DateKey <= endDate)
.Where(c => c.CompanyID == companyID).GroupBy(m => new { m.Description })
.Select(g => new DescriptionAmountModel { Amount = g.Sum(a => a.Amount).Value, Description = g.Key.Description })
.OrderByDescending(x => x.Amount);
I have like 15 of these calls on the dashboard and it can get very slow at times from what I imagine are multiple calls when in reality the database only needs to be queried once then that result needs to be queried for different results.
How can I do this?
Any help would be greatly appreciated
In your current solution each query executes separatly, on the same data. You can first execute the shared parts of the queries and bring the results from database. In your examples it is these where conditions
//Executes in database
var entities = dashboardEntity.FactSales.Where(d => d.DateKey >= startDate)
.Where(d => d.DateKey <= endDate)
.Where(c => c.CompanyID == companyID)
.ToList();
Now that this data is filtered to only what you want you can in memory do the rest of the aggregations:
//Happens in the List<T> in memory
ViewBag.companySalesTotal = entities.Sum(a => a.Amount);
var companyStoreTotalItem = entities.GroupBy(m => new { m.Description })
.Select(g => new DescriptionAmountModel { Amount = g.Sum(a => a.Amount).Value, Description = g.Key.Description })
.OrderByDescending(x => x.Amount);
This way you can make efficient. This make the query execute single time in database and rest of the part happen on the pullout in memory data
var result = dashboardEntity.FactSales.Where(d => d.DateKey >= startDate && d => d.DateKey <= endDate && d.CompanyID == companyID).ToList();
ViewBag.companySalesTotal = result.Sum(a => a.Amount);
//then get list of all items sold from in memory data
var companyStoreTotalItem = result.GroupBy(m => new { m.Description }).Select(g => new DescriptionAmountModel { Amount = g.Sum(a => a.Amount).Value, Description = g.Key.Description }).OrderByDescending(x => x.Amount);
I have a table of lessons, and I want to perform a text search over several fields of it. However the search should be ordered: for example lesson have a Keywords field and Description field. The search should give a priority over values found by Keywords. Everything should be also ordered by date but only after the priority is considered.
I'm also using ToPagedList() in the end from https://github.com/troygoode/PagedList (I think it just uses Skip() and Top() to manage pages)
This is what I have so far:
string[] word = /*Search words*/
var data = db.LessonsLearneds.Where(dbRecord => words.Any(word =>
dbRecord.SearchKeywords.StartsWith(word + ",") ||
dbRecord.SearchKeywords.Contains("," + word + ",") ||
dbRecord.SearchKeywords.EndsWith("," + word)))
.Select(x => new { Record = x, Order = 1 });
data = data.Union(
db.LessonsLearneds
.Where(dbRecord => words.Any(word => dbRecord.Title.Contains(word)))
.Select(x => new { Record = x, Order = 2 }));
data = data.Union(
db.LessonsLearneds
.Where(dbRecord => words.Any(word => dbRecord.Description.Contains(word)))
.Select(x => new { Record = x, Order = 3}));
data = data.Union(
db.LessonsLearneds
.Where(dbRecord => words.Any(word => dbRecord.Lesson.Contains(word)))
.Select(x => new { Record = x, Order = 4 }));
return data
.Distinct()
.OrderBy(x => x.Order)
.ThenByDescending(x => x.Record.Date)
.Select(x => x.Record)
.ToPagedList(pageNumber, pageSize);
Overall this code does almost what I want, except of Distinct(). Each union here can retrieve the same record, so I may receive it several times, and Distinct() does not forces the uniqueness because of virtual Order field. I cannot put Distinct after Select(x => x.Record) because of ToPagedList(..) which requires the set to be ordered (results in: The method 'Skip' is only supported for sorted input in LINQ to Entities. exception)
Any ideas?
I have one so far: to add Order field after I Distinct, but this means that I will have to write those Contains checks twice which I think is very ugly solution.
First, since you are projecting unique records due to the different Order value, replace the Union operator with Concat (which is the LINQ equivalent of the SQL UNION ALL).
string[] word = /*Search words*/
var data = db.LessonsLearneds.Where(dbRecord => words.Any(word =>
dbRecord.SearchKeywords.StartsWith(word + ",") ||
dbRecord.SearchKeywords.Contains("," + word + ",") ||
dbRecord.SearchKeywords.EndsWith("," + word)))
.Select(x => new { Record = x, Order = 1 });
data = data.Concat(
db.LessonsLearneds
.Where(dbRecord => words.Any(word => dbRecord.Title.Contains(word)))
.Select(x => new { Record = x, Order = 2 }));
data = data.Concat(
db.LessonsLearneds
.Where(dbRecord => words.Any(word => dbRecord.Description.Contains(word)))
.Select(x => new { Record = x, Order = 3}));
data = data.Concat(
db.LessonsLearneds
.Where(dbRecord => words.Any(word => dbRecord.Lesson.Contains(word)))
.Select(x => new { Record = x, Order = 4 }));
Then replace the Distinct with GroupBy using x.Record as a key and taking min Order for each grouping, and do the rest as in your current query:
return data
.GroupBy(x => x.Record)
.Select(g => new { Record = g.Key, Order = g.Min(x => x.Order) })
.OrderBy(x => x.Order)
.ThenByDescending(x => x.Record.Date)
.Select(x => x.Record)
.ToPagedList(pageNumber, pageSize);
You can replace Distinct with GroupBy and Select, like this:
return data
.GroupBy(x => x.Record)
.Select(g => g.OrderBy(x => x.Order).ThenByDescending(x => x.Record.Date).First())
.OrderBy(x => x.Order)
.ThenByDescending(x => x.Record.Date)
.Select(x => x.Record)
.ToPagedList(pageNumber, pageSize);
The unfortunate side effect of this approach is that you need to repeat OrderBy inside the first Select, but it should produce the results that you are looking for.
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();