2015-02-11 3 views
4

У меня есть этот запрос t-sql из старой системы, которая работает на Cold Fusion. Этот запрос занимает менее одной секунды, чтобы вернуть записи.Медленная производительность в запросе LINQ

select dateDiff(month, dateAdd(hour, 11, createdAt), {ts '2015-02-28 23:59:59'}) p, count(*) c 
from account 
where createdAt <= {ts '2015-02-28 23:59:59'} 
and accountType = 'business' 
and dateDiff(month, dateAdd(hour, 11, createdAt), {ts '2015-02-28 23:59:59'}) <12 
group by dateDiff(month, dateAdd(hour, 11, createdAt), {ts '2015-02-28 23:59:59'}) 
order by dateDiff(month, dateAdd(hour, 11, createdAt), {ts '2015-02-28 23:59:59'}) 

Я теперь преобразовываю это в новую систему с использованием .NET и LINQ. Мне удалось написать этот запрос LINQ, который дает мне те же результаты.

from a in db.Accounts 
where SqlFunctions.DateDiff("Month", SqlFunctions.DateAdd("Hour", 11, a.createdAt), "2015-02-28 23:59:59") < 12 
&& a.accountType == "business" 
group a by SqlFunctions.DateDiff("Month", a.createdAt, "2015-02-28 23:59:59") into grp 
orderby SqlFunctions.DateDiff("Month", grp.FirstOrDefault().createdAt, "2015-02-28 23:59:59") 
select new ProgressViewModel.Data 
{ 
    date = SqlFunctions.DateDiff("Month", grp.FirstOrDefault().createdAt, "2015-02-28 23:59:59"), 
    amount = grp.Count() 
}); 

Тем не менее, этот запрос занимает не менее 5 секунд, чтобы работать, в то время как с первым (T-SQL) она занимает менее 1 секунды.

Используя Glimpse, мы можем увидеть t-sql, который генерирует запрос LINQ. Он имеет несколько подвыборок, и он в 5 раз длиннее быстрого запроса.

Как я могу улучшить запрос LINQ?

+0

есть ли шанс включения выше запроса к хранимой процедуре и возвращает результат и отображать в сетке? если да, то это более эффективный способ – Immu

+4

'grp.FirstOrDefauilt()' выглядит подозрительно для одного - разве вы не имели в виду 'grp.Key'? Кроме того, почему не 'dateAdd' передаваемый параметр, а не значение в базе данных? Это должно позволить вам использовать индекс на 'createdAt', если он есть. – Luaan

+0

Не могли бы вы сообщить нам, как выглядит сгенерированный запрос LINQ? – JunaidKirkire

ответ

1

Я действительно сомневаюсь, что вы действительно хотите использовать FirstOrDefault() в любой точке вашего кода.

BTW Похоже, вы используете LinqToSQL в качестве поставщика Linq. Эта штука отвратительна, неэффективна и абсолютно ошибочна. Вы должны переключиться на EntityFramework, если его вообще можно

Учитывая, что ... возможно, вы должны попробовать это ...

var date = new Date(2015, 2, 28).AddDays(1); 
var query = from account in context.Accounts 
      where account.CreatedAt < date 
      where account.accountType == "business" 
      group account by 
        SqlFunctions.DateDiff(
          "Month", 
          SqlFunctions.DateAdd(
            "Hour", 11, a.createdAt), 
          date) 
      into g 
      where g.Key < 12 
      order by g.Key ascending 
      select new 
      { 
       MonthsAgo = g.Key, 
       Count = g.Count(), 
      }; 
+0

Привет, Арон, ваше решение было самым чистым, и оно работало так, как ожидалось. Однако нет такой вещи, как преобразование из linq в EF. Это разные вещи, в нашем случае мы используем EF и linq для запроса db (через EF). Во всяком случае, спасибо за ваше решение, выбрав g.Key и добавив, где g.Key <12 сделал всю разницу. Благодарю. – WPalombini

2

Попробуйте что-то вроде этого, чтобы привести его в памяти до группировки:

from ca in (
    from a in db.Accounts 
    where SqlFunctions.DateDiff("Month", SqlFunctions.DateAdd("Hour", 11, a.createdAt), "2015-02-28 23:59:59") < 12 && a.accountType == "business" 
    select a.createdAt).ToArray() 
group a by new /* month diff */ into grp 
orderby grp.Key 
select new ProgressViewModel.Data 
{ 
    date = grp.key, 
    amount = grp.Count() 
}); 
+0

Чертовски ... без поддержки. Просто понял, что это довольно плохой запрос, так как его сделано в памяти – Aron

+1

@Aron - Почему это плохо, чтобы быть в памяти? Довольно часто это быстрее, чем не вносить его. Это действительно зависит от того, что вы пытаетесь оптимизировать. – Enigmativity

+0

Итак, в этом случае вы могли бы, по крайней мере, преобразовать учетные записи в нечто с более низкими накладными расходами. – Aron

0

Принимая быстрый взгляд я бы исследовать ваш раздел grp.FirstOrDefault - это действительно то, что вы хотите сделать?

-1

В этом случае я определенно пошёл бы за параметризованной Хранимой процедурой. Вы также должны рассмотреть возможность создания индекса покрытия для таблиц, которые вам нужны. Эти шаги обычно повышают производительность с помощью очень заметной суммы.

Смежные вопросы