Вы писали:
Я хочу запросить за последние 365 дней, сколько дней появился клиент и сколько минут он потратил
Ваше требование неоднозначно:Если клиент появляется 5 января 2019 23:58 и уезжает 6 января 2019 00:02, сколько дней он появляется?2 дня?
Система управления базами данных (СУБД) гораздо лучше подходит для больших запросов, чем ваш локальный процесс.Поэтому, если у вас есть выбор, постарайтесь, чтобы ваша СУБД выполнила всю работу, и перенесли только те данные, которые вы фактически планируете использовать, в локальный процесс.
Увы, вы забыли показать нам свои классы.Из вашего описания кажется, что у вас есть таблица Customers
и таблица, представляющая их Appearances
.Если вы используете каркас сущностей, классы будут похожи на следующие:
class Customer
{
public int Id {get; set;}
... // other properties
// every Customer has zero or more Appearances (one-to-many)
public virtual ICollection<Appearance> Appearances {get; set;}
}
class Appearance
{
public int Id {get; set;}
public DateTime StartTime {get; set;} // Customer appears
public DateTime EndTime {get; set;} // Customer goes away
... // other properties
// every appearance belongs to exactly one Customer, using foreign key
public int CustomerId {get; set;}
public virtual Customer Customer {get; set;}
}
Если вы используете каркас сущностей, ваш запрос прост:
TimeSpan last365Days = TimeSpan.FromDays(365);
DateTime startTime = DateTime.UtcNow-last365Days;
var query = dbContext.Customers
.Where(customer => ...) // only if you don't want all Customers
.Select(customer => new
{
// select only the Customer properties you actually plan to use
Id = Customer.Id,
Name = Customer.Name,
// total time spent in ticks (consider using seconds, minutes, ...)
TimeSpentTicks = customer.Appearances
// keep only appearances in the last 365 days
.Where(appearance.StartTime >= startTime)
// the time spent during this appearance in ticks
.Select(appearance => (appearance.EndTime - appearance.StartTime).Ticks)
// Sum these ticks
.Sum(),
// to calculate the number of days:
// from StartTime and EndTime take the day number of the year
// keep distinct day number
// and count the number of distinct day numbers
NumberOfAppearanceDays = customer.Appearances
.SelectMany(appearance => new
{
appearance.StartTime.DayOfYear,
appearance.EndTime.DayOfYear,
})
.Distinct()
.Count(),
});
Если вы не используете сущностьFramework, но какой-то другой метод, который может обрабатывать IQueryable<...>
, вам придется присоединиться к группе самостоятельно
IQueryable<Customer> customers = ...
IQueryable<Appearance> appearances = ...
.Where(appearance.StartTime >= startTime);
var query = customers.GroupJoin(appearances, // GroupJoin customers and appearances
customer => customer.Id, // from every customer take the Id
appearance => appearance.CustomerId, // from every appearance take the CustomerId
(customer, appearances) => new // from every customer with all his
{ // appearances, make one new object
Id = Customer.Id,
Name = Customer.Name,
TimeSpentTicks = appearances
.Select(appearance => (appearance.EndTime - appearance.StartTime).Ticks)
.Sum(),
NumberOfAppearanceDays = appearances
.SelectMany(appearance => new
{
appearance.StartTime.DayOfYear,
appearance.EndTime.DayOfYear,
})
.Distinct()
.Count(),
});
Подождите!ты еще не закончил!У вас есть время, проведенное в Tick, вам придется конвертировать их в TimeSpans
.Обычно вы используете для этого TimeSpan.FromTicks(...)
, но SQL этого не знает.Теперь, когда вы ограничили данные данными, которые вы фактически планируете использовать, вы можете переместить выбранные данные в локальный процесс и затем использовать TimeSpan.FromTicks(...)
Продолжение запроса:
.AsEnumerable()
.Select(fetchedCustomerInfo => new
{
Id = fetchedCustomerInfo.Id,
...
TimeSpent = TimeSpent.FromTicks(fetchedCustomerInfo.TimeSpentTicks),
NumberOfAppearanceDays = fetchedCustomerInfo.NumberOfAppearanceDays,
});
Возможно, вы даже не можете обработать IQueryable<...>
, в этом случае вам придется использовать Dapper и SQL для выполнения GroupJoin.Поиск в StackOverflow о том, как сделать GroupJoin.Вы должны быть в состоянии составить оператор SQL из кода выше