2015-01-23 3 views
2

Я использую Entity Framework 6.0 с локальным SQL-сервером SQL Server, я попытался использовать функцию Enumerable.Contains, но я нашел ее очень медленной, когда у нас есть большой список.Entity Framework 6: медленное выражение «WHERE NOT IN»

Я исследовал и обнаружил, что существует рекомендация использовать операторы SQL для этого случая, как описано в: Why is .Contains slow? Most efficient way to get multiple entities by primary key?.

Теперь я задаюсь вопросом, почему следующее утверждение настолько медленное, что занимает более 180 секунд, чтобы запустить «ToList()»?

using (var context = new MyDbContext()) 
{ 
    Random rand = new Random(); 
    var ids = new List<int>(); 
    for (int i = 0; i < 20000; i++) 
     ids.Add(rand.Next(550000)); 

    Stopwatch watch = new Stopwatch(); 
    watch.Start(); 

    var values = new StringBuilder(); 
    values.AppendFormat("{0}", ids[0]); 
    for (int i = 1; i < ids.Count; i++) 
     values.AppendFormat(", {0}", ids[i]); 

    var sql = string.Format(
     "SELECT * FROM [MyDb].[dbo].[MyEntities] WHERE NOT [ID] IN ({0})", 
     values); 

    var result = context.Set<MyEntity>().SqlQuery(sql).ToList(); 

    watch.Stop(); 
    var msec = watch.ElapsedMilliseconds; 
} 

Обратите внимание, что инструкция похожа на ссылку test3 выше, за исключением того, что мы включили термин «NOT».

Извините, мой плохой английский ^^.

+0

** Предупреждение ** ваш код уязвим для потенциальных атак на SQL-атаку! –

+3

Сколько времени требуется, чтобы выполнить этот sql напрямую? –

+0

Вы используете более новую версию Entityframework, которая имеет лучшую поддержку для [Enumerable.Contains] (http://blogs.msdn.com/b/adonet/archive/2013/10/17/ef6-rtm-available.aspx). –

ответ

1

Отправка очень большого условия фильтрации в базу данных, как вы делаете, может быть очень медленной. В зависимости от размера таблицы может быть намного быстрее получить все значения и выполнить фильтрацию в памяти, используя HashSet. Попробуйте следующее:

Random rand = new Random(); 
var set= new HashSet<int>(); 
for (int i = 0; i < 20000; i++) 
    set.Add(rand.Next(550000)); 

Stopwatch watch = new Stopwatch(); 
watch.Start(); 

var sql = "SELECT * FROM [MyDb].[dbo].[MyEntities]"; 

var result = context.Set<MyEntity>() 
      .SqlQuery(sql) 
      .AsEnumerable() 
      .Where(x => !set.Contains(x.ID)) 
      .ToList(); 

watch.Stop(); 
+0

Если я выполняю инструкцию непосредственно на сервере sql, то в первый раз я заработаю за 11 секунд, но я проверю ваше предложение. –

+0

Также, если в таблице содержится много столбцов, вы извлекаете только те, которые вам действительно нужны, это может сэкономить много времени, используя это решение. – Magnus

+0

Таблица имеет 19 столбцов. Пожалуйста, узнайте, почему эта абсурдная разница во времени? Если я запускаю непосредственно на сервере sql: 11 секунд. Структура сущности с использованием SQL-оператора: 226 секунд. –

0

При использовании EF для выполнения ваших запросов времени выполнения сочетает в себе несколько вещей:

  • время для создания и перевода в запросе
  • времени, чтобы выполнить запрос и получить результаты
  • времени материализовать запрос результаты для юридических лиц

Используя SQL напрямую с помощью метода SqlQuery, вы упускаете время для создания запроса и перевода его в SQL.

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

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

+0

Моя сущность содержит 10399 строк. Этот запрос возвращает 330 строк em 226 segundos. –

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