Группировка данных в памяти с помощью Linq To Objects или с использованием собственного SQL - ЧТО БЫСТРЕЕ? - PullRequest
2 голосов
/ 09 июля 2010

Я заметил, что LINQ to Objects имеет метод GroupBy. В свете этого мне было интересно, может ли кто-нибудь создать случай для группового списка объектов в памяти, используя LINQ против того, чтобы SQL Server выполнял группировку?

Ответы [ 5 ]

5 голосов
/ 09 июля 2010

Некоторые причины, по которым вы можете сгруппировать данные с помощью LINQ на клиенте:

  1. Ваши объекты уже находятся в памяти.
  2. Возможно, вы захотите группировать по разным ключам в разное время в течение жизненного цикла данных, и обратные поездки в базу данных могут быть дорогостоящими.
  3. Возможно, вы захотите сгруппировать что-то, что неудобно для вычисления SQL Server, например data.GroupBy(d => DoSomethingComplicatedWith(d)).
3 голосов
/ 09 июля 2010

В большом наборе данных с правильно проиндексированными таблицами SQL Server будет работать быстрее. Каждый раз. Руки вниз. Для небольших наборов данных вы можете даже не заметить разницу.

1 голос
/ 09 июля 2010

Существуют (не слишком мало) ситуации, когда ваши объекты не содержатся в базе данных.

1 голос
/ 09 июля 2010

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

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

0 голосов
/ 24 января 2017

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

Запрос возвращает около 1,5 млн записей, сгруппированных в более чем 15000 групп.

Группировка в SQL

allArticleConcepts = DataAccess.ArticleConceptRepository.AllNoTracking
    .Join(DataAccess.ArticleAnalysisDataRepository.AllNoTracking.Where(aa => aa.CommentCount >= minCommentCount), 
        outer => outer.ArticleId, 
        inner => inner.ArticleId, 
        (outer, inner) => outer)
    .Where(ac => missingXData.Any(x => x.ArticleId == ac.ArticleId))
    .GroupBy(ac => ac.ArticleId)
    .ToDictionary(grp => grp.Key, grp => grp
        .Select(ac => new Concept { ContextSynLexemId = ac.LexemId, LexemId = ac.LexemId, Frequency = ac.Freq })
        .ToList());
  • Продолжительность SQL = 22 секунды
  • Общая продолжительность = 23 секунды

Сгенерированный запрос очень сложен и поэтому занимает много времени.

Группировка в .NET

allArticleConcepts = DataAccess.ArticleConceptRepository.AllNoTracking
    .Join(DataAccess.ArticleAnalysisDataRepository.AllNoTracking.Where(aa => aa.CommentCount >= minCommentCount), 
        outer => outer.ArticleId, 
        inner => inner.ArticleId, 
        (outer, inner) => outer)
    .Where(ac => missingXData.Any(x => x.ArticleId == ac.ArticleId))
    .ToList()
    .GroupBy(ac => ac.ArticleId)
    .ToDictionary(grp => grp.Key, grp => grp
        .Select(ac => new Concept { ContextSynLexemId = ac.LexemId, LexemId = ac.LexemId, Frequency = ac.Freq })
        .ToList());
  • Продолжительность SQL = 13 секунд
  • Общая продолжительность = 15 секунд

Сгенерированный SQL намного проще и быстрее. Однако вычисления на C # немного медленнее.

Группировка в .NET с некоторым параллельным ароматом

allArticleConcepts = DataAccess.ArticleConceptRepository.AllNoTracking
    .Join(DataAccess.ArticleAnalysisDataRepository.AllNoTracking.Where(aa => aa.CommentCount >= minCommentCount), 
        outer => outer.ArticleId, 
        inner => inner.ArticleId, 
        (outer, inner) => outer)
    .Where(ac => missingXData.Any(x => x.ArticleId == ac.ArticleId))
    .ToList()
    .AsParallel()
    .GroupBy(ac => ac.ArticleId)
    .ToDictionary(grp => grp.Key, grp => grp
        .Select(ac => new Concept { ContextSynLexemId = ac.LexemId, LexemId = ac.LexemId, Frequency = ac.Freq })
        .ToList());

Это дает незначительное улучшение на стороне клиента.

В качестве заключения о том, как выполнить группировку:

  • если логика не может быть переведена в SQL: Linq2Objects является обязательным
  • если логика достаточно сложна и / или имеет дело с большим количеством сущностей, Linq2Objects, скорее всего, будет правильным путем
  • Если логика довольно проста и индексы могут быть использованы, Linq2SQL - наиболее вероятный путь. Однако это также требует тщательного написания LINQ, чтобы избежать генерации неэффективных запросов.

Примечание: если запрос касается большого числа объектов, но результат относительно невелик, можно рассмотреть прямое выполнение запроса (или хранимую процедуру) и сопоставить результат с объектами. Это обеспечивает максимальную гибкость при написании запроса и минимизацию времени обхода C # - SQL Server.

...