LINQ查询检索轮转数据需要太长时间

时间:2017-11-15 20:16:55

标签: c# sql linq pivot

我正在研究一个LINQ查询,其中包含一些数据透视数据,如下所示

var q = data.GroupBy(x => new
            {
                x.Med.Name,
                x.Med.GenericName,             
            }).ToList().Select(g =>
                new SummaryDto
                {
                    Name= g.Key.Name,
                    GenericName = g.Key.GenericName,                    
                    Data2012 = g.Where(z => z.ProcessDate.Year == 2012).Count(),
                    Data2013 = g.Where(z => z.ProcessDate.Year == 2013).Count(),
                    Data2014 = g.Where(z => z.ProcessDate.Year == 2014).Count(),
                    Data2015 = g.Where(z => z.ProcessDate.Year == 2015).Count(),
                    Data2016 = g.Where(z => z.ProcessDate.Year == 2016).Count(),
                    Data2017 = g.Where(z => z.ProcessDate.Year == 2017).Count(),
                    TotalCount = g.Count(),
                }).AsQueryable();

            return q;

上述LINQ查询grp q.Count()*6 times时间过长。如果有10000条记录,则查询60000次 有没有更好的方法让它更快?

2 个答案:

答案 0 :(得分:1)

将年份添加到组密钥,然后再次分组,并按组收集计数:

return data.GroupBy(x => new {
    x.Med.Name
,   x.Med.GenericName
,   x.ProcessDate.Year
}).Select(g => new {
    g.Key.Name
,   g.Key.GenericName
,   g.Key.Year
,   Count = g.Count()
}).GroupBy(g => new {
    g.Name
,   g.GenericName
}).Select(g => new SummaryDto {
    Name = g.Key.Name
,   GenericName = g.Key.GenericName
,   Data2012 = g.SingleOrDefault(x => x.Year == 2012)?.Count ?? 0
,   Data2013 = g.SingleOrDefault(x => x.Year == 2013)?.Count ?? 0
,   Data2014 = g.SingleOrDefault(x => x.Year == 2014)?.Count ?? 0
,   Data2015 = g.SingleOrDefault(x => x.Year == 2015)?.Count ?? 0
,   Data2016 = g.SingleOrDefault(x => x.Year == 2016)?.Count ?? 0
,   Data2017 = g.SingleOrDefault(x => x.Year == 2017)?.Count ?? 0
,   TotalCount = g.Sum(x => x.Count)
}).AsQueryable();

注意:这种方法存在问题,因为年份在SummaryDto类中是硬编码的。你最好将你的DTO构造函数IDictionary<int,int>传递给每年的计数。如果您进行此更改,最终的Select(...)将如下所示:

.Select(g => new SummaryDto {
    Name = g.Key.Name
,   GenericName = g.Key.GenericName
,   TotalCount = g.Sum(x => x.Count)
,   DataByYear = g.ToDictionary(i => i.Year, i => i.Count)
}).AsQueryable();

答案 1 :(得分:1)

我建议按年份在组内进行分组,然后转换为字典以访问计数。是否更快地按年份分组然后计入内存取决于初始分组的分布,但是对于数据库,它可能取决于它按年分组的效率,因此我将测试以确定哪个似乎最快。

在任何情况下,初始分组后的年份分组比内存中的查询快约33%,但同样它在很大程度上取决于分布。随着初始组的数量增加,按年份查询的分组减慢以匹配原始查询。请注意,没有任何年份计数的原始查询大约是时间的1/3。

以下是数据库分组后的分组:

var q = data.GroupBy(x => new {
    x.Med.Name,
    x.Med.GenericName,
}).ToList().Select(g => {
    var gg = g.GroupBy(d => d.ProcessDate.Year).ToDictionary(d => d.Key, d => d.Count());
    return new SummaryDto {
        Name = g.Key.Name,
        GenericName = g.Key.GenericName,
        Data2012 = gg.GetValueOrDefault(2012),
        Data2013 = gg.GetValueOrDefault(2013),
        Data2014 = gg.GetValueOrDefault(2014),
        Data2015 = gg.GetValueOrDefault(2015),
        Data2016 = gg.GetValueOrDefault(2016),
        Data2017 = gg.GetValueOrDefault(2017),
        TotalCount = g.Count(),
    };
}).AsQueryable();