2014-03-29 71 views
2

我試圖獲取更改的記錄,其中車輛的點火改變。這是我的SQL表。相關的LINQ查詢優化

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查詢?

+0

可以有成千上萬的車輛記錄。將所有內容加載到內存中會使其非常緩慢。 –

+0

你不知道沒有嘗試。這完全取決於你的設計。在關係型數據庫中查詢這種類型的順序數據(例如時間序列)也不會很有效 - 您需要爲每條記錄運行一個子查詢。我會着眼於編寫一個使用遊標的存儲過程(是的 - 這是對遊標的完全有效使用),甚至可以編寫[CLR SQL存儲過程](http://msdn.microsoft.com/zh-cn/我們/庫/ 5czye81z(v = VS.90)的.aspx)。 –

+0

請在您的屏幕截圖上突出顯示「47890」和「47879」的記錄。自己找到它們會導致眼睛疲勞。 – Neolisk

回答

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項目是擠滿了擴展方法,就像我在這裏粗略提出的擴展方法。

+0

這將從db中獲取所有記錄,並將diff作爲linq執行到對象查詢。 – MarcinJuraszek

+0

我知道,我在答覆中說過,而且我也說過它什麼時候仍然有用。 – Wasp

+0

同意Marcin。是的,我已經做了這樣的事情。將所有內容都帶入記憶中。但是我有一輛車大約有五萬到六萬輛的記錄,並且帶來一切都需要相當長的時間。那就是爲什麼,我正在尋找一些只能從db中帶來更改記錄的東西。 –

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

這很好,但它也假定了id總是連續的(我的意思是,沒有漏洞),這可能是好的,但可能不是。 – Wasp

+0

正確地說黃蜂。這是實際情況,可能有其他車輛記錄在-1地方。這就是我使用日期的原因。 –