Ускорение этого LINQ to SQL кода - PullRequest
1 голос
/ 01 апреля 2011

Я занимаюсь разработкой ASP.NET, где мне нужно отображать каждую покупку для каждой учетной записи в GridView (поэтому, в основном, каждая покупка связана с учетной записью).

Проблема в том, что у нас более 6000 поставщиков (при условии, что их можно отфильтровать до 1000, что обычно будет делать клиент), у каждой из которых будет несколько транзакций.Таким образом, вы можете себе представить, что время, необходимое для привязки этих данных, очень велико - фактически, время от времени SQL-сервер отключается.К сожалению, я не могу использовать подкачку страниц, так как все данные должны отображаться на одной странице.

То, что я делаю, похоже на код ниже (не на моей машине, поэтому не могу скопировать точно)

IEnumerable<Account> accs = (from s in dc.Accounts select s);
foreach (Account acc in accs)
{
    IEumerable<Purchases> purchs = (from s in dc.Purchases where s.AccountID == acc.ID select s);
    double 30daysval;
    double 60daysval;
    foreach (Purchases purch in Purchases)
    {
        TimeSpan span = DateTime.Now - purch.Timestamp;
        if (span.Days <= 30)
        {
            30daysval += purch.Value;
            //Add a row to the grid
        }
        else if (span.Days <= 60)
        {
            60daysval += purch.Value
            //Add a row to the grid
        }
    }
    //Add total row for that account
}

Есть ли более быстрый способ сделать это, возможно, используя соединения в LINQ или что-то еще?Я знаю, что это может показаться немного безнадежным, поскольку объем данных огромен, и что отображение нескольких тысяч строк на одной странице довольно абсурдно, но это то, что мне сказали сделать ...

Любые идеи или помощь будет высоко ценится

Ответы [ 5 ]

2 голосов
/ 01 апреля 2011
// Assuming that there is no fk keys.  Else you don't need joins.
var Purchases = dc.Accounts
    .Join(dc.Purchases, a => a.Id, p => p.AccountId, (a,p) => new {a,p})
    .Select(p);

double 30daysval;
double 60daysval;
foreach (Purchases purch in Purchases)
{
    TimeSpan span = DateTime.Now - purch.Timestamp;
    if (span.Days <= 30)
    {
        30daysval += purch.Value;
        //Add a row to the grid
    }
    else if (span.Days <= 60)
    {
        60daysval += purch.Value
        //Add a row to the grid
    }
}
1 голос
/ 01 апреля 2011

Прежде всего, вам необходимо профилировать код SQL, отправляемый в БД.

Вполне возможно, что для каждой запрашиваемой вами учетной записи в БД отправляется новый запрос.загрузить ваши покупки для этого аккаунта.Это называется отложенной загрузкой или отложенной загрузкой.Так как они все равно вам нужны, это не очень эффективно.

Предполагая, что у вас есть нужные внешние ключи, вы можете использовать LoadOptions, чтобы убедиться, что выполняется только один запрос.

пример:

DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Account >(a => a.Purchases );
context.LoadOptions = options;

Таким образом, все покупки напрямую загружаются в аккаунты, и это может сэкономить много времени.

1 голос
/ 01 апреля 2011
var resuts =                 (from acc in dc.Accounts
                             join purchase in dc.Purchases on 
                             acc.ID equals purchase.AccountID
                             select new {Account = acc, Purchase = purchase}).ToList();


daysVal30 = results.Where(x=>(DateTime.Now - x.Purchase.Timestamp).Days <= 30).Sum(x=>x.Purchase.Value);
daysVal60 = results.Where(x=>(DateTime.Now - x.Purchase.Timestamp).Days > 30 && (DateTime.Now - x.Purchase.Timestamp).Days <=60).Sum(x=>x.Purchase.Purchase.Value);

grid.DataSource = results;
0 голосов
/ 05 апреля 2011

Большое спасибо всем, кто ответил, ваши предложения мне очень помогли. Одна вещь, которую я забыл упомянуть, которая была проблемой, это то, что пользователь может захотеть увидеть аккаунты без покупок.

В итоге я создал собственный класс, содержащий объект Account и список объектов Shopping. Затем я выполняю внешнее соединение в LINQ, чтобы выбрать каждую учетную запись и их покупки, и сохранить результаты в этом новом классе.

0 голосов
/ 01 апреля 2011

Объединение - это главное, но поможет еще одна вещь - Linq to SQL (и Linq to Entities) не переведет манипулирование датой в SQL, но они могут сравнить постоянную дату со значением.

В данный момент вы ищете (сейчас - отметка времени> 30 дней) - я предлагаю вам изменить это и искать (сейчас - 30 дней> отметка времени). Вам нужно потренироваться (сейчас - 30 дней) в C #, потому что выражение не будет переводиться, но затем вы можете отфильтровать строки на уровне базы данных перед циклом:

// Assuming that there is no fk keys.  Else you don't need joins.
var Purchases = dc.Accounts
    .Join(dc.Purchases, a => a.Id, p => p.AccountId, (a,p) => new {a,p})
    .Select(p); //this is not evaluated yet

var todayAnd30 = DateTime.Now.AddDays(30); //happens instantly in memory
var todayAnd60 = DateTime.Now.AddDaye(60); //happens instantly in memory

var purchases30 = Purchases.Where(p => p.Timestamp > todayAnd30); //will translate to SQL
var purchases60 = Purchases.Where(p => p.Timestamp > todayAnd60; //will translate to SQL

foreach (Purchases purch in purchases30) //purchases30 query hits database at this point
{
   30daysval += purch.Value;
   //any other processing
}

foreach (Purchases purch in purchases60) //purchases60 query hits database at this point
{
   60daysval += purch.Value;
   //any other processing
}

Благодарю Holystream, я использовал соединения из вашего ответа в качестве отправной точки для сохранения ввода!

...