实体框架6 - 按时分组按First()排序需要太长

时间:2015-11-01 17:54:26

标签: c# mysql entity-framework group-by sql-order-by

我真的需要这个帮助,经过几个小时的搜索后找不到相关的答案。

MySQL,Entity Framework 6,拥有数百万条记录的数据库,记录如下:

Indexint(11)NOT NULL
TaskIDint(11)NOT NULL
DeviceIDbigint(20)NOT NULL
Commentslongtext NULL
ExtendedResultslongtext NULL
RunResultint(11)NOT NULL
JobResultint(11)NOT NULL
JobResultValuedouble NOT NULL
ReporterIDbigint(20)NOT NULL
FieldIDbigint(20)NOT NULL
TimeOfRundatetime NOT NULL

我需要的是获取特定任务ID的所有记录,然后按DeviceID分组并按TimeOfRun排序,以获取特定任务ID中每个deviceID的最新数据。

这是我的代码:

List<JobsRecordHistory> newH = db.JobsRecordHistories.AsNoTracking().Where(x => x.TaskID == taskID).GroupBy(x => x.DeviceID).
                Select(x => x.OrderByDescending(y => y.TimeOfRun).FirstOrDefault()).ToList();

但这是生成的查询:

{SELECT

Apply1IndexApply1TaskIDApply1DEVICEID1 AS DeviceIDApply1RunResultApply1JobResultApply1JobResultValueApply1ExtendedResultsApply1CommentsApply1ReporterIDApply1FieldIDApply1TimeOfRun FROM(选择 Project2p__linq__0Project2DeviceID, (选择 Project3Index 来自JobsRecordHistories AS Project3  WHERE(Project3TaskID = @ p__linq__0)AND(Project2DeviceID = Project3DeviceID)  订购 Project3TimeOfRun DESC限制1)AS Index, (选择 Project3TaskID 来自JobsRecordHistories AS Project3  WHERE(Project3TaskID = @ p__linq__0)AND(Project2DeviceID = Project3DeviceID)  订购 Project3TimeOfRun DESC限制1)AS TaskID, (选择 Project3DeviceID 来自JobsRecordHistories AS Project3  WHERE(Project3TaskID = @ p__linq__0)AND(Project2DeviceID = Project3DeviceID)  订购 Project3TimeOfRun DESC限制1)AS DEVICEID1, (选择 Project3RunResult 来自JobsRecordHistories AS Project3  WHERE(Project3TaskID = @ p__linq__0)AND(Project2DeviceID = Project3DeviceID)  订购 Project3TimeOfRun DESC限制1)AS RunResult, (选择 Project3JobResult 来自JobsRecordHistories AS Project3  WHERE(Project3TaskID = @ p__linq__0)AND(Project2DeviceID = Project3DeviceID)  订购 Project3TimeOfRun DESC限制1)AS JobResult, (选择 Project3JobResultValue 来自JobsRecordHistories AS Project3  WHERE(Project3TaskID = @ p__linq__0)AND(Project2DeviceID = Project3DeviceID)  订购 Project3TimeOfRun DESC限制1)AS JobResultValue, (选择 Project3ExtendedResults 来自JobsRecordHistories AS Project3  WHERE(Project3TaskID = @ p__linq__0)AND(Project2DeviceID = Project3DeviceID)  订购 Project3TimeOfRun DESC限制1)AS ExtendedResults, (选择 Project3Comments 来自JobsRecordHistories AS Project3  WHERE(Project3TaskID = @ p__linq__0)AND(Project2DeviceID = Project3DeviceID)  订购 Project3TimeOfRun DESC限制1)AS Comments, (选择 Project3ReporterID 来自JobsRecordHistories AS Project3  WHERE(Project3TaskID = @ p__linq__0)AND(Project2DeviceID = Project3DeviceID)  订购 Project3TimeOfRun DESC限制1)AS ReporterID, (选择 Project3FieldID 来自JobsRecordHistories AS Project3  WHERE(Project3TaskID = @ p__linq__0)AND(Project2DeviceID = Project3DeviceID)  订购 Project3TimeOfRun DESC限制1)AS FieldID, (选择 Project3TimeOfRun 来自JobsRecordHistories AS Project3  WHERE(Project3TaskID = @ p__linq__0)AND(Project2DeviceID = Project3DeviceID)  订购 Project3TimeOfRun DESC限制1)AS TimeOfRun FROM(选择 @ p__linq__0 AS p__linq__0Distinct1DeviceID FROM(SELECT DISTINCT Extent1DeviceID 来自JobsRecordHistories AS Extent1  WHERE Extent1TaskID = @ p__linq__0)AS Distinct1)AS Project2)AS Apply1}

这太长了。
我承认,我不太了解SQL,但是如果在WHERE语句之后插入一个ToList(),那么我会更快地得到结果,尽管它仍然不是正确的事情,因为有很多不需要的数据在这种情况下数据库传递给我的应用程序,对于40k记录,它仍然很慢= 30秒。

我也试过这个:

Dictionary<long, DateTime> DeviceIDAndTime = db.JobsRecordHistories.AsNoTracking().Where(x => x.TaskID == taskID).GroupBy(x => x.DeviceID)
                .Select(g => new DeviceIDaAndTime { deviceID = g.Key, timeOfRun = g.Max(gi => gi.TimeOfRun) }).ToDictionary(x => x.deviceID, x => x.timeOfRun);

为了以这种方式使用字典:

                List<JobsRecordHistory> newH = db.JobsRecordHistories.AsNoTracking().Where(x => DeviceIDAndTime.Keys.Contains(x.DeviceID) && x.TimeOfRun == DeviceIDAndTime[x.DeviceID]).ToList();

但是我收到了这个错误:

Additional information: LINQ to Entities does not recognize the method 'System.DateTime get_Item(Int64)' method, and this method cannot be translated into a store expression.

根据我的理解,这是有道理的,在将timeOfRun与字典值进行比较时,LINQ在编写查询时需要特定的值而不是集合。

我很奇怪,我没有找到任何相关的帖子,而其他人没有遇到这个问题。我想我错过了什么。

感谢任何帮助,谢谢

2 个答案:

答案 0 :(得分:2)

最终弄明白并提高了表现 我需要一个查询和一个子查询,而且我需要MAX函数而不是ORDER因为我不关心结果的顺序,我只关心最大的(timeOfRun)。 此外,一旦我注意到更大的索引列(我的PK,自动增量)意味着更新近的数据,事情就被简化了,所以我不需要MAX(timeOfRun),而是使用了MAX(索引),尽管我使用了MAX(索引)。我相信它会以同样的方式运作。

这是我的LINQ:

var historyQuery = db.JobsRecordHistories.AsNoTracking().Where(y => y.TaskID == taskID &&
                                    db.JobsRecordHistories.Where(x => x.TaskID == taskID).GroupBy(x => x.DeviceID).Select(g => g.Max(i => i.Index)).Contains<int>(y.Index));

这是生成的SQL:

{SELECT

Extent1IndexExtent1TaskIDExtent1DeviceIDExtent1RunResultExtent1JobResultExtent1JobResultValueExtent1ExtendedResultsExtent1CommentsExtent1ReporterIDExtent1FieldIDExtent1TimeOfRun 来自JobsRecordHistories AS Extent1  WHERE(Extent1TaskID = @ p__linq__0)AND(EXISTS(SELECT) 1 AS C1 FROM(选择 Extent2DeviceID AS K1, MAX(Extent2Index)AS A1 来自JobsRecordHistories AS Extent2  在Extent2TaskID = @ p__linq__1  通过...分组 Extent2DeviceID)AS GroupBy1  在GroupBy1A1 = Extent1Index)}}

我希望这会有所帮助,因为我花了1.5天的谷歌搜索,查看SQL查询,LINQ,调试和优化

答案 1 :(得分:1)

提供查询语法而不是基于镜头的方法。
我没有在本地测试过,但你可能会看到改进的sql生成 或者至少也许这种方法可能会让你走上正确的道路

using System;
using System.Data.Entity;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace EF.CodeFirst
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            using (var db = new TestDbContext())
            {
                var taskId = 1;
                var query = from job in db.JobRecordHistories
                    where job.TaskId == taskId
                    orderby job.TimeOfRun descending
                    group job by job.DeviceId
                    into deviceGroup
                    select deviceGroup;

                foreach (var deviceGroup in query)
                {
                    foreach (var jobRecordHistory in deviceGroup)
                    {
                        Console.WriteLine("DeviceId '{0}', TaskId'{1}' Runtime'{2}'", jobRecordHistory.DeviceId,
                            jobRecordHistory.TaskId, jobRecordHistory);
                    }
                }
            }
        }
    }

    public class TestDbContext : DbContext
    {
        public DbSet<JobRecordHistory> JobRecordHistories { get; set; }
    }

    public class JobRecordHistory
    {
        public int Id { get; set; }
        public int TaskId { get; set; }
        public int DeviceId { get; set; }
        public DateTime TimeOfRun { get; set; }
    }
}