Агрегирование данных с помощью C # или SQL - PullRequest
0 голосов
/ 18 декабря 2018

Мне нужно получить данные за последний 1 год, в которых около 3 миллионов строк в течение ок.30000 уникальных клиентов.Я ищу способ узнать за последние 365 дней, сколько дней появился клиент и сколько минут он провел?Другими словами, мне нужно агрегировать данные по каждому клиенту за год и вычислять среднее значение на основе количества дней, в течение которых появился клиент.

Подход, который я ищу:

Получить все строкиданные (которые имеют около 3 миллионов строк и 10 столбцов) из таблицы и загружаются в память.Выполните на нем запрос LINQ - не могли бы вы помочь мне выяснить, какой будет наилучший подход к использованию запроса Group By или как я могу выполнить оптимизированные запросы, чтобы получить средние данные для примерно 30000 клиентов.Я думаю разделить 3 миллиона строк на две коллекции (клиенты только с севера и юга против востока и запада) при извлечении данных из таблицы.В настоящее время у меня уходит около 60 секунд, чтобы получить все данные из таблицы.

Среднее = Общее количество минут, потраченных клиентом за последние 365 дней / дней. Клиент появился за последние 365 дней.один раз, а затем выполнить все фильтры будет гораздо лучше подходить.Любые мысли / предложения приветствуются.

1 Ответ

0 голосов
/ 18 декабря 2018

Вы писали:

Я хочу запросить за последние 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 из кода выше

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...