使用LINQ在大型数据库中查询需要花费很多时间

时间:2015-04-09 17:01:36

标签: c# sql-server asp.net-mvc linq entity-framework

我正在使用LINQ在我的控制器中准备一些数据并将它们发送到视图中。 我的连接由EF6代码首次与sql server迁移 所以在控制器和LINQ表达式中,数据库模型映射到正确的视图模型如下:

        var temp = db.points.ToList().Select(pnt => new MapPointsModel()
        {
            pointId = pnt.pointId,
            name = pnt.name,
            positionX = pnt.positionX,
            positionY = pnt.positionY,
            road = pnt.road.id,
            order = pnt.order,
            signalState = pnt.signalState,
            powerState = pnt.powerState,
            videoState = pnt.videoState,
            cameraState = pnt.cameraState,
            hourTraffic = new int[]{
                pnt.crossings.Where(c => DateTime.Compare(c.dateTime, lastHour) >= 0 ).Where(c => c.line == 1).Count(),
                pnt.crossings.Where(c => DateTime.Compare(c.dateTime, lastHour) >= 0 ).Where(c => c.line == 2).Count(),
                pnt.crossings.Where(c => DateTime.Compare(c.dateTime, lastHour) >= 0 ).Where(c => c.line == 3).Count()
            },
            dayTraffic = new int[]{
                pnt.crossings.Where(c => DateTime.Compare(c.dateTime, lastDay) >= 0 ).Where(c => c.line == 1).Count(),
                pnt.crossings.Where(c => DateTime.Compare(c.dateTime, lastDay) >= 0 ).Where(c => c.line == 2).Count(),
                pnt.crossings.Where(c => DateTime.Compare(c.dateTime, lastDay) >= 0 ).Where(c => c.line == 3).Count()
            },
            hourViolation = new int[] {
                pnt.crossings.Where(c => c.violation != null && DateTime.Compare(c.dateTime, lastHour) >= 0).Where(c => c.line == 1).Count(),
                pnt.crossings.Where(c => c.violation != null && DateTime.Compare(c.dateTime, lastHour) >= 0).Where(c => c.line == 2).Count(),
                pnt.crossings.Where(c => c.violation != null && DateTime.Compare(c.dateTime, lastHour) >= 0).Where(c => c.line == 3).Count()
            },
            dayViolation = new int[] {
                pnt.crossings.Where(c => c.violation != null && DateTime.Compare(c.dateTime, lastDay) >= 0).Where(c => c.line == 1).Count(),
                pnt.crossings.Where(c => c.violation != null && DateTime.Compare(c.dateTime, lastDay) >= 0).Where(c => c.line == 2).Count(),
                pnt.crossings.Where(c => c.violation != null && DateTime.Compare(c.dateTime, lastDay) >= 0).Where(c => c.line == 3).Count()
            },
            checkedViolations = pnt.crossings.Where(c => c.violation != null).Where(c => c.violation.deliberated == true).Count(),
            uncheckedViolations = pnt.crossings.Where(c => c.violation != null).Where(c => c.violation.deliberated == false).Count(),
            bandAvgSpeed = new int[] {
                pnt.crossings.Where(c => c.line == 1).Count() == 0 ? 0 : pnt.crossings.Where(c => c.line == 1).Sum(c => c.speed)/pnt.crossings.Where(c => c.line == 1).Count(),
                pnt.crossings.Where(c => c.line == 2).Count() == 0 ? 0 : pnt.crossings.Where(c => c.line == 2).Sum(c => c.speed)/pnt.crossings.Where(c => c.line == 2).Count(),
                pnt.crossings.Where(c => c.line == 3).Count() == 0 ? 0 : pnt.crossings.Where(c => c.line == 3).Sum(c => c.speed)/pnt.crossings.Where(c => c.line == 3).Count(),
            },
        });
        return temp.ToList();

此代码适用于10000条或更低的记录,但是在500000条或更多条记录中没有结果,并且在所有测试中都超时有效。

我正在寻找这个问题的原因

更新:

“点数”表中只有4条记录,超过500000条记录的较大记录是“交叉点”但是我尝试从.toList()删除db.points来解决问题,但它上升了例外:

System.ArgumentException: Argument types do not match

5 个答案:

答案 0 :(得分:2)

你的问题是:

db.points.ToList()....

这将从表中检索所有记录到内存中。因此,您拥有的记录越多,所需的时间就越长。您需要创建一个只返回所需记录的查询。

我不确定你打算一次性使用500,000条记录。你只需要一个子集吗?如果是这样,那么做这样的事情:

db.points.Select(....).Take(25) // or however many you need.

您还在进行大量的子选择和子计数,每个子计数都是单独的语句,因此对于那些500,000,您实际上可能有数百万个子查询。

答案 1 :(得分:0)

您正在将其转换为List。这一直在占用。尝试从db.points.ToList()中删除ToList()。选择。这应该可以解决你的问题。

在使用ToList转换它之前,它仍然是IQueryable并且不会命中数据库。当你执行ToList时,它将查询数据库,在内存中获取结果,然后处理内存中的记录。

答案 2 :(得分:0)

这样做

    var temp = db.points.ToList()....

将整个表格带到客户端,所以如果你有几兆的数据,这可能需要一段时间,具体取决于cpu /连接/内存

答案 3 :(得分:0)

您需要更改代码中的ToList()并更改var temp = db.points.AsNoTracking()这样可以节省您的时间。另外,为您的代码创建预先生成的视图,可以提供更好的性能。

答案 4 :(得分:0)

主要问题是平均!!!

因为平均函数在所有记录上运行4次,所以大约需要7秒。

我更改了linq查询,而不是一次请求所有数据,而是使用OrderBy GroupBy工具从服务器的其他请求中计算的平均部分。然后合并两个从服务器检索的列表。

此外,我从points表中移除了.ToList()(如人们建议的那样)并将数组结构更改为简单变量(这导致了错误!)。

此解决方案导致运行时间仅为1秒而不是7秒,具有相同的输出:)