2013-12-19 30 views
3

我有一個簡單的表像這樣:如何SQL轉換爲LINQ與前瞻(導致類似查詢)

[Timestamp] DATETIME, 
[Value] NVARCHAR(50) 

所有[時間戳值是唯一的,但[超值]是不是唯一的。

我想查找何時每個[Value]開始和停止。我有以下T-SQL(SQL 2008),這似乎工作正常,儘管任何改進建議都將受到讚賞,因爲在2012年之前的T-SQL中實現領導運營商的模式很好。

SELECT 
    h1.[Value] AS 'Event ID', 
    MIN(h1.[Timestamp]) AS 'Start Time', 
    h2.[Timestamp] AS 'End Time', 
    DATEDIFF(second, MIN(h1.[Timestamp]), h2.[Timestamp]) AS 'Duration (sec)' 
FROM 
    #RawHistory h1 
    INNER JOIN 
    #RawHistory h2 
    ON h2.[Timestamp] = 
    ( 
     SELECT TOP 1 
      h3.[Timestamp] 
     FROM 
      #RawHistory h3 
     WHERE 
      h3.[Value] <> h1.[Value] 
      AND 
      h3.[Timestamp] > h1.[Timestamp] 
     ORDER BY 
      h3.[Timestamp] ASC 
    ) 
GROUP BY 
    h1.[Value], h2.[Timestamp] 
ORDER BY 
    MIN(h1.[Timestamp]) 

因此,鑑於此輸入:

Timestamp    Value 
======================= ====== 
2013-12-18 20:26:00.000 Violet 
2013-12-18 20:30:00.000 Red 
2013-12-18 20:35:00.000 Red 
2013-12-18 20:40:00.000 Orange 
2013-12-18 20:50:00.000 Yellow 
2013-12-18 21:00:00.000 Green 
2013-12-18 21:05:00.000 Green 
2013-12-18 21:07:00.000 Green 
2013-12-18 21:10:00.000 Blue 
2013-12-18 21:20:00.000 Indigo 
2013-12-18 21:30:00.000 Violet 
2013-12-18 21:30:05.000 Violet 
2013-12-18 21:40:00.000 Red 
2013-12-18 21:50:00.000 Orange 
2013-12-18 22:00:00.000 Yellow 
2013-12-18 22:10:00.000 Green 

我希望這樣的輸出:

Event ID Start Time    End Time    Duration (sec) 
======== ======================= ======================= ============= 
Violet 2013-12-18 20:26:00.000 2013-12-18 20:30:00.000 240 
Red  2013-12-18 20:30:00.000 2013-12-18 20:40:00.000 600 
Orange 2013-12-18 20:40:00.000 2013-12-18 20:50:00.000 600 
Yellow 2013-12-18 20:50:00.000 2013-12-18 21:00:00.000 600 
Green 2013-12-18 21:00:00.000 2013-12-18 21:10:00.000 600 
Blue  2013-12-18 21:10:00.000 2013-12-18 21:20:00.000 600 
Indigo 2013-12-18 21:20:00.000 2013-12-18 21:30:00.000 600 
Violet 2013-12-18 21:30:00.000 2013-12-18 21:40:00.000 600 
Red  2013-12-18 21:40:00.000 2013-12-18 21:50:00.000 600 
Orange 2013-12-18 21:50:00.000 2013-12-18 22:00:00.000 600 
Yellow 2013-12-18 22:00:00.000 2013-12-18 22:10:00.000 600 

注意,不返回任何綠色的最後一個實例,因爲沒有「結束日期「。

但是,我真正感興趣的是能夠使用C#中的LINQ對類似的Values和Timestamps列表執行相同類型的查詢。不是針對SQL數據源,而是針對具有Value和Timestamp屬性的POCO對象列表。

所以,如果我有類似

class VT 
{ 
    string Value { get; set; } 
    DateTime Timestamp { get; set; } 
} 

類和我有一個

List<VT> vts 
這些對象的

,這將是在該名單將執行相同的LINQ的聲明轉型的類型?

我以前實現這個使用了跟蹤,當值變化的狀態機模式 - 這不是很明顯的是時間序列數據可能對在連續時間的值相同的多個條目,如

T1, Red 
T2, Red 
T3, Red 
T4, Yellow 
T5, Yellow 
T6, Blue 

等等。因爲SQL查找與當前值不同的下一個值的時間戳,所以SQL起作用。這可以使用Linq嗎?

可以肯定,與連續值上面的例子中,最終的結果應該是:

Red, T1, T4 
Yellow, T4, T6 

所以連續讀數應合併成一個單一的讀數。查看SQL查詢如何查找與展望中的當前記錄不同的值的下一條記錄。

我編輯了原始數據集示例以包含重複的連續值。輸出不會改變。

回答

3

您可以使用ZIP-Statement與一個元素跳過的第二個輸入相同的順序來實現這一點 - 這就是您的「預測」。但是,它沒有考慮到,可能有多個具有相同值的連續事件。

var firstList = new List<VT> { 
    new VT { Value = "Violet", Timestamp = new DateTime(2013, 12, 18, 20, 26, 0) }, 
    new VT { Value = "Red", Timestamp = new DateTime(2013, 12, 18, 20, 30, 0) }, 
    new VT { Value = "Orange", Timestamp = new DateTime(2013, 12, 18, 20, 40, 0) }, 
    new VT { Value = "Yellow", Timestamp = new DateTime(2013, 12, 18, 20, 50, 0) } 
}; 

var secondList = firstList.Skip(1); 

var combined = firstList.Zip(secondList, 
           (first, second) => new { 
             EventID = first.Value, 
             StartTime = first.Timestamp, 
             EndTime = second.Timestamp, 
             Duration = (second.Timestamp - first.Timestamp).TotalSeconds}); 
} 

這導致

EventID StartTime   EndTime    Duration 
Violet 18.12.2013 20:26:00 18.12.2013 20:30:00 240 
Red  18.12.2013 20:30:00 18.12.2013 20:40:00 600 
Orange 18.12.2013 20:40:00 18.12.2013 20:50:00 600 

與可以很容易地使用反應性擴展(RX)來解決相同的值的多個連續事件的問題。只需將firstList和secondList的類型替換爲IObservable<VT>即可。然後.Subscribe()合併,以獲得一個流完成事件,每當一個顏色chages。致電DistinctUntilChanged將確保只傳遞具有不同顏色的事件。

var firstList = new List<VT> { 
    new VT { Value = "Violet", Timestamp = new DateTime(2013, 12, 18, 20, 26, 0) }, 
    new VT { Value = "Violet", Timestamp = new DateTime(2013, 12, 18, 20, 27, 0) }, 
    new VT { Value = "Violet", Timestamp = new DateTime(2013, 12, 18, 20, 29, 0) }, 
    new VT { Value = "Red", Timestamp = new DateTime(2013, 12, 18, 20, 30, 0) }, 
    new VT { Value = "Red", Timestamp = new DateTime(2013, 12, 18, 20, 34, 0) }, 
    new VT { Value = "Orange", Timestamp = new DateTime(2013, 12, 18, 20, 40, 0) }, 
    new VT { Value = "Yellow", Timestamp = new DateTime(2013, 12, 18, 20, 50, 0) } 
} 
.ToObservable().DistinctUntilChanged(vt => vt.Value); 

var secondList = firstList.Skip(1); 

var combined = firstList.Zip(secondList, (first, second) => new { 
               EventID = first.Value, 
               StartTime = first.Timestamp, 
               EndTime = second.Timestamp, 
               Duration = (second.Timestamp - first.Timestamp).TotalSeconds}); 

combined.Subscribe(); 

這將返回預期結果。

DistinctUntilChanged只是根據值從流中移除所有相同的事件,因此這有效地減少了上面LINQ示例中對輸入的輸入。

+0

我可以爲secondList的Skip()子句添加Where子句,告訴它跳到列表中與第一個列表中元素的值不同的下一個元素?如果是這樣,那看起來如何?看到我對這個問題的澄清。我不一定在尋找下一個記錄,而是下一個記錄有不同的值。 – noelhx

+0

我不知道這是否可以用標準LINQ操作符完成,但一般問題在此解決:http://msdn.microsoft.com/vstudio/cc138361.aspx – okrumnow

+0

原來,使用Reactive Extensions時,問題通過添加一個簡單的'DistinctUntilChanged'來解決。 – okrumnow