Как уже говорилось, может быть несколько причин для выбора группировки на стороне клиента (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.