简体   繁体   中英

Correlated LINQ Query Optimization

I'm trying to fetch the changed records, in which the Ignition of Vehicle Changed. Here is my SQL table.

在此处输入图片说明

ie I want to fetch record 47890 and and record 47879.

i've written the following correlated LINQ query.

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();

It gives the following exception:

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

Inner Exception Says:

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

I hope this is due to the query being very inefficient and taking too much time to execute. How can we optimize the LINQ query?

It is all right, you could probably do something more efficient with a nice sproc or some magic SQL. But let's say you can't, for whatever reason. It does not mean you have to either give up or run some SELECT N+1 code. If you properly order your sequence (as you are already doing) you can scan your sequence just once with Linq and use some smart extension method to remember the previous value. With something like this in your toolbox:

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;
        }
    }
}

You can then write an expression like this:

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;

This scans in memory all your records, but just once, and it does it in an expressive way. As said, doing it at the database level would be more efficient, but maybe you can't go there.

The MoreLinq project is packed with extension methods like the one I roughly suggest here.

Do a self join:

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();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM