相关的LINQ查询优化

时间:2014-03-29 09:14:08

标签: c# linq

我试图获取已更改的记录,其中车辆点火已更改。这是我的SQL表。

enter image description here

即我想获取记录47890并记录47879。

我已经编写了以下相关的LINQ查询。

var sData = (from log in db.GSMDeviceLogs
             where log.Vehicle.VehicleId == vehicleId
             where log.IgnitionOn != (from prevLog in db.GSMDeviceLogs
                                     where prevLog.Vehicle.VehicleId == vehicleId
                                     where prevLog.DateTimeOfLog < log.DateTimeOfLog
                                     orderby prevLog.DateTimeOfLog descending
                                     select prevLog.IgnitionOn).FirstOrDefault()
             orderby log.DateTimeOfLog ascending
             select new { LogId = log.GSMDeviceLogId,
                          Ignition = log.IgnitionOn,
                          Date = log.DateTimeOfLog,
                          Location = log.Location }).ToList();

它提供以下例外:

An exception of type 'System.Data.Entity.Core.EntityCommandExecutionException' occurred in EntityFramework.SqlServer.dll but was not handled in user code

内部异常说:

{"Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding."}

我希望这是因为查询效率非常低并且执行时间太长。我们如何优化LINQ查询?

2 个答案:

答案 0 :(得分:0)

没关系,你可以用一个漂亮的sproc或一些神奇的SQL做一些更高效的事情。但是,不管出于何种原因,我们都说你不能。这并不意味着您必须放弃或运行一些SELECT N + 1代码。如果您正确订购了序列(正如您已经在做的那样),您可以使用Linq扫描序列一次,并使用一些智能扩展方法来记住以前的值。在工具箱中使用类似的东西:

static class EnumerableExtensions
{
    public static IEnumerable<TR> Pair<T, TR>(this IEnumerable<T> source, Func<T, T, TR> resultor)
    {
        return PairImpl<T, TR>(source, resultor);
    }

    static IEnumerable<TR> PairImpl<T, TR>(IEnumerable<T> source, Func<T, T, TR> resultor)
    {
        var e = source.GetEnumerator();
        var a = default(T);

        while (e.MoveNext())
        {
            var b = e.Current;
            yield return resultor(a, b);
            a = b;
        }
    }
}

然后你可以写一个这样的表达式:

var logs = 
    from log in db.GSMDeviceLogs
    where log.Vehicle.VehicleId == vehicleId
    orderby log.DateTimeOfLog ascending
    select log;

var diffs = 
    from log in logs.Pair((a, b) => new { a, b })
    where log.a != null && log.a.IgnitionOn != log.b.IgnitionOn
    select log.a;

这会扫描内存中的 所有记录,但只扫描一次,然后以富有表现力的方式完成。如上所述,在数据库级别执行它会更有效,但也许你不能去那里。

MoreLinq项目包含了扩展方法,就像我在这里粗略建议的那样。

答案 1 :(得分:0)

自我加入:

var sData = (from log in db.GSMDeviceLogs
             join log2 in db.GSMDeviceLogs on log.GSMDeviceLogId equals log2.GSMDeviceLogId - 1
             where log.Vehicle.VehicleId == vehicleId
                 && log.IgnitionOn != log2.IgnitionOn
             orderby log.DateTimeOfLog ascending
             select new { LogId = log.,
                          Ignition = log.IgnitionOn,
                          Date = log.DateTimeOfLog,
                          Location = log.Location }).ToList();